diff --git a/.gitignore b/.gitignore
index 4c9e220..53a2368 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,5 @@
ipch/
Debug/
Release/
+dshow_base/strmbase_d.lib
+dshow_base/strmbase_r.lib
diff --git a/dshow_base/.vs/baseclasses/v17/Browse.VC.db b/dshow_base/.vs/baseclasses/v17/Browse.VC.db
new file mode 100644
index 0000000..159157a
Binary files /dev/null and b/dshow_base/.vs/baseclasses/v17/Browse.VC.db differ
diff --git a/dshow_base/.vs/baseclasses/v17/Solution.VC.db b/dshow_base/.vs/baseclasses/v17/Solution.VC.db
new file mode 100644
index 0000000..0cf564b
Binary files /dev/null and b/dshow_base/.vs/baseclasses/v17/Solution.VC.db differ
diff --git a/dshow_base/BaseClasses.vcxproj b/dshow_base/BaseClasses.vcxproj
new file mode 100644
index 0000000..a40c702
--- /dev/null
+++ b/dshow_base/BaseClasses.vcxproj
@@ -0,0 +1,354 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Debug_W
+ Win32
+
+
+ Debug_W
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+ Release_W
+ Win32
+
+
+ Release_W
+ x64
+
+
+
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}
+ BaseClasses
+ Win32Proj
+
+
+
+ StaticLibrary
+ MultiByte
+
+
+ StaticLibrary
+ MultiByte
+
+
+ StaticLibrary
+ Unicode
+
+
+ StaticLibrary
+ Unicode
+
+
+ StaticLibrary
+ MultiByte
+
+
+ StaticLibrary
+ MultiByte
+
+
+ StaticLibrary
+ Unicode
+
+
+ StaticLibrary
+ Unicode
+
+
+ MultiByte
+
+
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>10.0.30319.1
+ Debug\
+ Debug\
+ Release\
+ Release\
+ $(Configuration)\
+ $(Configuration)\
+ $(Configuration)\
+ $(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+
+
+
+ Disabled
+ .;%(AdditionalIncludeDirectories)
+ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+
+ Level3
+ EditAndContinue
+
+
+ strmiids.lib;%(AdditionalDependencies)
+ $(OutDir)strmbasd.lib
+ %(AdditionalLibraryDirectories)
+
+
+
+
+ MaxSpeed
+ .;%(AdditionalIncludeDirectories)
+ WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+
+
+ Level3
+ ProgramDatabase
+
+
+ strmiids.lib;%(AdditionalDependencies)
+ $(OutDir)strmbase.lib
+
+
+
+
+ Disabled
+ .;%(AdditionalIncludeDirectories)
+ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+
+ Level3
+ EditAndContinue
+
+
+ strmiids.lib;%(AdditionalDependencies)
+ $(OutDir)\..\strmbase_d.lib
+ %(AdditionalLibraryDirectories)
+
+
+
+
+ MaxSpeed
+ .;%(AdditionalIncludeDirectories)
+ WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+
+
+ Level3
+ ProgramDatabase
+
+
+ strmiids.lib;%(AdditionalDependencies)
+ $(OutDir)\..\strmbase_r.lib
+
+
+
+
+ X64
+
+
+ Disabled
+ .;%(AdditionalIncludeDirectories)
+ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+
+ Level3
+ ProgramDatabase
+
+
+ strmiids.lib;%(AdditionalDependencies)
+ $(OutDir)strmbasd.lib
+ %(AdditionalLibraryDirectories)
+
+
+
+
+ X64
+
+
+ MaxSpeed
+ .;%(AdditionalIncludeDirectories)
+ WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+
+
+ Level3
+ ProgramDatabase
+
+
+ strmiids.lib;%(AdditionalDependencies)
+ $(OutDir)strmbase.lib
+
+
+
+
+ X64
+
+
+ Disabled
+ .;%(AdditionalIncludeDirectories)
+ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+
+ Level3
+ ProgramDatabase
+
+
+ strmiids.lib;%(AdditionalDependencies)
+ $(OutDir)strmbasd.lib
+ %(AdditionalLibraryDirectories)
+
+
+
+
+ X64
+
+
+ MaxSpeed
+ .;%(AdditionalIncludeDirectories)
+ WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+
+
+ Level3
+ ProgramDatabase
+
+
+ strmiids.lib;%(AdditionalDependencies)
+ $(OutDir)strmbase.lib
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dshow_base/BaseClasses.vcxproj.filters b/dshow_base/BaseClasses.vcxproj.filters
new file mode 100644
index 0000000..ca454fc
--- /dev/null
+++ b/dshow_base/BaseClasses.vcxproj.filters
@@ -0,0 +1,222 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/dshow_base/UpgradeLog.XML b/dshow_base/UpgradeLog.XML
new file mode 100644
index 0000000..4b64d9e
--- /dev/null
+++ b/dshow_base/UpgradeLog.XML
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dshow_base/UpgradeLog2.XML b/dshow_base/UpgradeLog2.XML
new file mode 100644
index 0000000..42cbdee
--- /dev/null
+++ b/dshow_base/UpgradeLog2.XML
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dshow_base/amextra.cpp b/dshow_base/amextra.cpp
new file mode 100644
index 0000000..af0de96
--- /dev/null
+++ b/dshow_base/amextra.cpp
@@ -0,0 +1,111 @@
+//------------------------------------------------------------------------------
+// File: AMExtra.cpp
+//
+// Desc: DirectShow base classes - implements CRenderedInputPin class.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include // DirectShow base class definitions
+#include // Needed for definition of timeGetTime
+#include // Standard data type limit definitions
+#include // Used for time critical log functions
+
+#include "amextra.h"
+
+#pragma warning(disable:4355)
+
+// Implements CRenderedInputPin class
+
+CRenderedInputPin::CRenderedInputPin(__in_opt LPCTSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName) :
+ CBaseInputPin(pObjectName, pFilter, pLock, phr, pName),
+ m_bAtEndOfStream(FALSE),
+ m_bCompleteNotified(FALSE)
+{
+}
+#ifdef UNICODE
+CRenderedInputPin::CRenderedInputPin(__in_opt LPCSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName) :
+ CBaseInputPin(pObjectName, pFilter, pLock, phr, pName),
+ m_bAtEndOfStream(FALSE),
+ m_bCompleteNotified(FALSE)
+{
+}
+#endif
+
+// Flush end of stream condition - caller should do any
+// necessary stream level locking before calling this
+
+STDMETHODIMP CRenderedInputPin::EndOfStream()
+{
+ HRESULT hr = CheckStreaming();
+
+ // Do EC_COMPLETE handling for rendered pins
+ if (S_OK == hr && !m_bAtEndOfStream) {
+ m_bAtEndOfStream = TRUE;
+ FILTER_STATE fs;
+ EXECUTE_ASSERT(SUCCEEDED(m_pFilter->GetState(0, &fs)));
+ if (fs == State_Running) {
+ DoCompleteHandling();
+ }
+ }
+ return hr;
+}
+
+
+// Called to complete the flush
+
+STDMETHODIMP CRenderedInputPin::EndFlush()
+{
+ CAutoLock lck(m_pLock);
+
+ // Clean up renderer state
+ m_bAtEndOfStream = FALSE;
+ m_bCompleteNotified = FALSE;
+
+ return CBaseInputPin::EndFlush();
+}
+
+
+// Notify of Run() from filter
+
+HRESULT CRenderedInputPin::Run(REFERENCE_TIME tStart)
+{
+ UNREFERENCED_PARAMETER(tStart);
+ m_bCompleteNotified = FALSE;
+ if (m_bAtEndOfStream) {
+ DoCompleteHandling();
+ }
+ return S_OK;
+}
+
+
+// Clear status on going into paused state
+
+HRESULT CRenderedInputPin::Active()
+{
+ m_bAtEndOfStream = FALSE;
+ m_bCompleteNotified = FALSE;
+ return CBaseInputPin::Active();
+}
+
+
+// Do stuff to deliver end of stream
+
+void CRenderedInputPin::DoCompleteHandling()
+{
+ ASSERT(m_bAtEndOfStream);
+ if (!m_bCompleteNotified) {
+ m_bCompleteNotified = TRUE;
+ m_pFilter->NotifyEvent(EC_COMPLETE, S_OK, (LONG_PTR)(IBaseFilter *)m_pFilter);
+ }
+}
+
diff --git a/dshow_base/amextra.h b/dshow_base/amextra.h
index a0f021f..5a861bf 100644
--- a/dshow_base/amextra.h
+++ b/dshow_base/amextra.h
@@ -1,56 +1,56 @@
-//------------------------------------------------------------------------------
-// File: AMExtra.h
-//
-// Desc: DirectShow base classes.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __AMEXTRA__
-#define __AMEXTRA__
-
-// Simple rendered input pin
-//
-// NOTE if your filter queues stuff before rendering then it may not be
-// appropriate to use this class
-//
-// In that case queue the end of stream condition until the last sample
-// is actually rendered and flush the condition appropriately
-
-class CRenderedInputPin : public CBaseInputPin
-{
-public:
-
- CRenderedInputPin(TCHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pName);
-#ifdef UNICODE
- CRenderedInputPin(CHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pName);
-#endif
-
- // Override methods to track end of stream state
- STDMETHODIMP EndOfStream();
- STDMETHODIMP EndFlush();
-
- HRESULT Active();
- HRESULT Run(REFERENCE_TIME tStart);
-
-protected:
-
- // Member variables to track state
- BOOL m_bAtEndOfStream; // Set by EndOfStream
- BOOL m_bCompleteNotified; // Set when we notify for EC_COMPLETE
-
-private:
- void DoCompleteHandling();
-};
-
-#endif // __AMEXTRA__
-
+//------------------------------------------------------------------------------
+// File: AMExtra.h
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __AMEXTRA__
+#define __AMEXTRA__
+
+// Simple rendered input pin
+//
+// NOTE if your filter queues stuff before rendering then it may not be
+// appropriate to use this class
+//
+// In that case queue the end of stream condition until the last sample
+// is actually rendered and flush the condition appropriately
+
+class CRenderedInputPin : public CBaseInputPin
+{
+public:
+
+ CRenderedInputPin(__in_opt LPCTSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName);
+#ifdef UNICODE
+ CRenderedInputPin(__in_opt LPCSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName);
+#endif
+
+ // Override methods to track end of stream state
+ STDMETHODIMP EndOfStream();
+ STDMETHODIMP EndFlush();
+
+ HRESULT Active();
+ HRESULT Run(REFERENCE_TIME tStart);
+
+protected:
+
+ // Member variables to track state
+ BOOL m_bAtEndOfStream; // Set by EndOfStream
+ BOOL m_bCompleteNotified; // Set when we notify for EC_COMPLETE
+
+private:
+ void DoCompleteHandling();
+};
+
+#endif // __AMEXTRA__
+
diff --git a/dshow_base/amfilter.cpp b/dshow_base/amfilter.cpp
new file mode 100644
index 0000000..03c13d1
--- /dev/null
+++ b/dshow_base/amfilter.cpp
@@ -0,0 +1,5358 @@
+//------------------------------------------------------------------------------
+// File: AMFilter.cpp
+//
+// Desc: DirectShow base classes - implements class hierarchy for streams
+// architecture.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+//=====================================================================
+//=====================================================================
+// The following classes are declared in this header:
+//
+//
+// CBaseMediaFilter Basic IMediaFilter support (abstract class)
+// CBaseFilter Support for IBaseFilter (incl. IMediaFilter)
+// CEnumPins Enumerate input and output pins
+// CEnumMediaTypes Enumerate the preferred pin formats
+// CBasePin Abstract base class for IPin interface
+// CBaseOutputPin Adds data provider member functions
+// CBaseInputPin Implements IMemInputPin interface
+// CMediaSample Basic transport unit for IMemInputPin
+// CBaseAllocator General list guff for most allocators
+// CMemAllocator Implements memory buffer allocation
+//
+//=====================================================================
+//=====================================================================
+
+#include
+#include
+
+#ifdef DXMPERF
+#include "dxmperf.h"
+#endif // DXMPERF
+
+
+//=====================================================================
+// Helpers
+//=====================================================================
+STDAPI CreateMemoryAllocator(__deref_out IMemAllocator **ppAllocator)
+{
+ return CoCreateInstance(CLSID_MemoryAllocator,
+ 0,
+ CLSCTX_INPROC_SERVER,
+ IID_IMemAllocator,
+ (void **)ppAllocator);
+}
+
+// Put this one here rather than in ctlutil.cpp to avoid linking
+// anything brought in by ctlutil.cpp
+STDAPI CreatePosPassThru(
+ __in_opt LPUNKNOWN pAgg,
+ BOOL bRenderer,
+ IPin *pPin,
+ __deref_out IUnknown **ppPassThru
+)
+{
+ *ppPassThru = NULL;
+ IUnknown *pUnkSeek;
+ HRESULT hr = CoCreateInstance(CLSID_SeekingPassThru,
+ pAgg,
+ CLSCTX_INPROC_SERVER,
+ IID_IUnknown,
+ (void **)&pUnkSeek
+ );
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ ISeekingPassThru *pPassThru;
+ hr = pUnkSeek->QueryInterface(IID_ISeekingPassThru, (void**)&pPassThru);
+ if (FAILED(hr)) {
+ pUnkSeek->Release();
+ return hr;
+ }
+ hr = pPassThru->Init(bRenderer, pPin);
+ pPassThru->Release();
+ if (FAILED(hr)) {
+ pUnkSeek->Release();
+ return hr;
+ }
+ *ppPassThru = pUnkSeek;
+ return S_OK;
+}
+
+
+
+#define CONNECT_TRACE_LEVEL 3
+
+//=====================================================================
+//=====================================================================
+// Implements CBaseMediaFilter
+//=====================================================================
+//=====================================================================
+
+
+/* Constructor */
+
+CBaseMediaFilter::CBaseMediaFilter(__in_opt LPCTSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ __in CCritSec *pLock,
+ REFCLSID clsid) :
+ CUnknown(pName, pUnk),
+ m_pLock(pLock),
+ m_clsid(clsid),
+ m_State(State_Stopped),
+ m_pClock(NULL)
+{
+}
+
+
+/* Destructor */
+
+CBaseMediaFilter::~CBaseMediaFilter()
+{
+ // must be stopped, but can't call Stop here since
+ // our critsec has been destroyed.
+
+ /* Release any clock we were using */
+
+ if (m_pClock) {
+ m_pClock->Release();
+ m_pClock = NULL;
+ }
+}
+
+
+/* Override this to say what interfaces we support and where */
+
+STDMETHODIMP
+CBaseMediaFilter::NonDelegatingQueryInterface(
+ REFIID riid,
+ __deref_out void ** ppv)
+{
+ if (riid == IID_IMediaFilter) {
+ return GetInterface((IMediaFilter *) this, ppv);
+ } else if (riid == IID_IPersist) {
+ return GetInterface((IPersist *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+/* Return the filter's clsid */
+STDMETHODIMP
+CBaseMediaFilter::GetClassID(__out CLSID *pClsID)
+{
+ CheckPointer(pClsID,E_POINTER);
+ ValidateReadWritePtr(pClsID,sizeof(CLSID));
+ *pClsID = m_clsid;
+ return NOERROR;
+}
+
+/* Override this if your state changes are not done synchronously */
+
+STDMETHODIMP
+CBaseMediaFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)
+{
+ UNREFERENCED_PARAMETER(dwMSecs);
+ CheckPointer(State,E_POINTER);
+ ValidateReadWritePtr(State,sizeof(FILTER_STATE));
+
+ *State = m_State;
+ return S_OK;
+}
+
+
+/* Set the clock we will use for synchronisation */
+
+STDMETHODIMP
+CBaseMediaFilter::SetSyncSource(__inout_opt IReferenceClock *pClock)
+{
+ CAutoLock cObjectLock(m_pLock);
+
+ // Ensure the new one does not go away - even if the same as the old
+ if (pClock) {
+ pClock->AddRef();
+ }
+
+ // if we have a clock, release it
+ if (m_pClock) {
+ m_pClock->Release();
+ }
+
+ // Set the new reference clock (might be NULL)
+ // Should we query it to ensure it is a clock? Consider for a debug build.
+ m_pClock = pClock;
+
+ return NOERROR;
+}
+
+/* Return the clock we are using for synchronisation */
+STDMETHODIMP
+CBaseMediaFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)
+{
+ CheckPointer(pClock,E_POINTER);
+ ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));
+ CAutoLock cObjectLock(m_pLock);
+
+ if (m_pClock) {
+ // returning an interface... addref it...
+ m_pClock->AddRef();
+ }
+ *pClock = (IReferenceClock*)m_pClock;
+ return NOERROR;
+}
+
+
+/* Put the filter into a stopped state */
+
+STDMETHODIMP
+CBaseMediaFilter::Stop()
+{
+ CAutoLock cObjectLock(m_pLock);
+
+ m_State = State_Stopped;
+ return S_OK;
+}
+
+
+/* Put the filter into a paused state */
+
+STDMETHODIMP
+CBaseMediaFilter::Pause()
+{
+ CAutoLock cObjectLock(m_pLock);
+
+ m_State = State_Paused;
+ return S_OK;
+}
+
+
+// Put the filter into a running state.
+
+// The time parameter is the offset to be added to the samples'
+// stream time to get the reference time at which they should be presented.
+//
+// you can either add these two and compare it against the reference clock,
+// or you can call CBaseMediaFilter::StreamTime and compare that against
+// the sample timestamp.
+
+STDMETHODIMP
+CBaseMediaFilter::Run(REFERENCE_TIME tStart)
+{
+ CAutoLock cObjectLock(m_pLock);
+
+ // remember the stream time offset
+ m_tStart = tStart;
+
+ if (m_State == State_Stopped){
+ HRESULT hr = Pause();
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+ m_State = State_Running;
+ return S_OK;
+}
+
+
+//
+// return the current stream time - samples with start timestamps of this
+// time or before should be rendered by now
+HRESULT
+CBaseMediaFilter::StreamTime(CRefTime& rtStream)
+{
+ // Caller must lock for synchronization
+ // We can't grab the filter lock because we want to be able to call
+ // this from worker threads without deadlocking
+
+ if (m_pClock == NULL) {
+ return VFW_E_NO_CLOCK;
+ }
+
+ // get the current reference time
+ HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // subtract the stream offset to get stream time
+ rtStream -= m_tStart;
+
+ return S_OK;
+}
+
+
+//=====================================================================
+//=====================================================================
+// Implements CBaseFilter
+//=====================================================================
+//=====================================================================
+
+
+/* Override this to say what interfaces we support and where */
+
+STDMETHODIMP CBaseFilter::NonDelegatingQueryInterface(REFIID riid,
+ __deref_out void **ppv)
+{
+ /* Do we have this interface */
+
+ if (riid == IID_IBaseFilter) {
+ return GetInterface((IBaseFilter *) this, ppv);
+ } else if (riid == IID_IMediaFilter) {
+ return GetInterface((IMediaFilter *) this, ppv);
+ } else if (riid == IID_IPersist) {
+ return GetInterface((IPersist *) this, ppv);
+ } else if (riid == IID_IAMovieSetup) {
+ return GetInterface((IAMovieSetup *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+#ifdef DEBUG
+STDMETHODIMP_(ULONG) CBaseFilter::NonDelegatingRelease()
+{
+ if (m_cRef == 1) {
+ KASSERT(m_pGraph == NULL);
+ }
+ return CUnknown::NonDelegatingRelease();
+}
+#endif
+
+
+/* Constructor */
+
+CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ __in CCritSec *pLock,
+ REFCLSID clsid) :
+ CUnknown( pName, pUnk ),
+ m_pLock(pLock),
+ m_clsid(clsid),
+ m_State(State_Stopped),
+ m_pClock(NULL),
+ m_pGraph(NULL),
+ m_pSink(NULL),
+ m_pName(NULL),
+ m_PinVersion(1)
+{
+#ifdef DXMPERF
+ PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );
+#endif // DXMPERF
+
+ ASSERT(pLock != NULL);
+}
+
+/* Passes in a redundant HRESULT argument */
+
+CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,
+ __in_opt LPUNKNOWN pUnk,
+ __in CCritSec *pLock,
+ REFCLSID clsid,
+ __inout HRESULT *phr) :
+ CUnknown( pName, pUnk ),
+ m_pLock(pLock),
+ m_clsid(clsid),
+ m_State(State_Stopped),
+ m_pClock(NULL),
+ m_pGraph(NULL),
+ m_pSink(NULL),
+ m_pName(NULL),
+ m_PinVersion(1)
+{
+#ifdef DXMPERF
+ PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );
+#endif // DXMPERF
+
+ ASSERT(pLock != NULL);
+ UNREFERENCED_PARAMETER(phr);
+}
+
+#ifdef UNICODE
+CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,
+ __in_opt LPUNKNOWN pUnk,
+ __in CCritSec *pLock,
+ REFCLSID clsid) :
+ CUnknown( pName, pUnk ),
+ m_pLock(pLock),
+ m_clsid(clsid),
+ m_State(State_Stopped),
+ m_pClock(NULL),
+ m_pGraph(NULL),
+ m_pSink(NULL),
+ m_pName(NULL),
+ m_PinVersion(1)
+{
+#ifdef DXMPERF
+ PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );
+#endif // DXMPERF
+
+ ASSERT(pLock != NULL);
+}
+CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,
+ __in_opt LPUNKNOWN pUnk,
+ __in CCritSec *pLock,
+ REFCLSID clsid,
+ __inout HRESULT *phr) :
+ CUnknown( pName, pUnk ),
+ m_pLock(pLock),
+ m_clsid(clsid),
+ m_State(State_Stopped),
+ m_pClock(NULL),
+ m_pGraph(NULL),
+ m_pSink(NULL),
+ m_pName(NULL),
+ m_PinVersion(1)
+{
+#ifdef DXMPERF
+ PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );
+#endif // DXMPERF
+
+ ASSERT(pLock != NULL);
+ UNREFERENCED_PARAMETER(phr);
+}
+#endif
+
+/* Destructor */
+
+CBaseFilter::~CBaseFilter()
+{
+#ifdef DXMPERF
+ PERFLOG_DTOR( L"CBaseFilter", (IBaseFilter *) this );
+#endif // DXMPERF
+
+ // NOTE we do NOT hold references on the filtergraph for m_pGraph or m_pSink
+ // When we did we had the circular reference problem. Nothing would go away.
+
+ delete[] m_pName;
+
+ // must be stopped, but can't call Stop here since
+ // our critsec has been destroyed.
+
+ /* Release any clock we were using */
+ if (m_pClock) {
+ m_pClock->Release();
+ m_pClock = NULL;
+ }
+}
+
+/* Return the filter's clsid */
+STDMETHODIMP
+CBaseFilter::GetClassID(__out CLSID *pClsID)
+{
+ CheckPointer(pClsID,E_POINTER);
+ ValidateReadWritePtr(pClsID,sizeof(CLSID));
+ *pClsID = m_clsid;
+ return NOERROR;
+}
+
+/* Override this if your state changes are not done synchronously */
+STDMETHODIMP
+CBaseFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)
+{
+ UNREFERENCED_PARAMETER(dwMSecs);
+ CheckPointer(State,E_POINTER);
+ ValidateReadWritePtr(State,sizeof(FILTER_STATE));
+
+ *State = m_State;
+ return S_OK;
+}
+
+
+/* Set the clock we will use for synchronisation */
+
+STDMETHODIMP
+CBaseFilter::SetSyncSource(__in_opt IReferenceClock *pClock)
+{
+ CAutoLock cObjectLock(m_pLock);
+
+ // Ensure the new one does not go away - even if the same as the old
+ if (pClock) {
+ pClock->AddRef();
+ }
+
+ // if we have a clock, release it
+ if (m_pClock) {
+ m_pClock->Release();
+ }
+
+ // Set the new reference clock (might be NULL)
+ // Should we query it to ensure it is a clock? Consider for a debug build.
+ m_pClock = pClock;
+
+ return NOERROR;
+}
+
+/* Return the clock we are using for synchronisation */
+STDMETHODIMP
+CBaseFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)
+{
+ CheckPointer(pClock,E_POINTER);
+ ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));
+ CAutoLock cObjectLock(m_pLock);
+
+ if (m_pClock) {
+ // returning an interface... addref it...
+ m_pClock->AddRef();
+ }
+ *pClock = (IReferenceClock*)m_pClock;
+ return NOERROR;
+}
+
+
+
+// override CBaseMediaFilter Stop method, to deactivate any pins this
+// filter has.
+STDMETHODIMP
+CBaseFilter::Stop()
+{
+ CAutoLock cObjectLock(m_pLock);
+ HRESULT hr = NOERROR;
+
+ // notify all pins of the state change
+ if (m_State != State_Stopped) {
+ int cPins = GetPinCount();
+ for (int c = 0; c < cPins; c++) {
+
+ CBasePin *pPin = GetPin(c);
+ if (NULL == pPin) {
+ break;
+ }
+
+ // Disconnected pins are not activated - this saves pins worrying
+ // about this state themselves. We ignore the return code to make
+ // sure everyone is inactivated regardless. The base input pin
+ // class can return an error if it has no allocator but Stop can
+ // be used to resync the graph state after something has gone bad
+
+ if (pPin->IsConnected()) {
+ HRESULT hrTmp = pPin->Inactive();
+ if (FAILED(hrTmp) && SUCCEEDED(hr)) {
+ hr = hrTmp;
+ }
+ }
+ }
+ }
+
+#ifdef DXMPERF
+ PERFLOG_STOP( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );
+#endif // DXMPERF
+
+ m_State = State_Stopped;
+ return hr;
+}
+
+
+// override CBaseMediaFilter Pause method to activate any pins
+// this filter has (also called from Run)
+
+STDMETHODIMP
+CBaseFilter::Pause()
+{
+ CAutoLock cObjectLock(m_pLock);
+
+ // notify all pins of the change to active state
+ if (m_State == State_Stopped) {
+ int cPins = GetPinCount();
+ for (int c = 0; c < cPins; c++) {
+
+ CBasePin *pPin = GetPin(c);
+ if (NULL == pPin) {
+ break;
+ }
+
+ // Disconnected pins are not activated - this saves pins
+ // worrying about this state themselves
+
+ if (pPin->IsConnected()) {
+ HRESULT hr = pPin->Active();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+ }
+ }
+
+
+#ifdef DXMPERF
+ PERFLOG_PAUSE( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );
+#endif // DXMPERF
+
+ m_State = State_Paused;
+ return S_OK;
+}
+
+// Put the filter into a running state.
+
+// The time parameter is the offset to be added to the samples'
+// stream time to get the reference time at which they should be presented.
+//
+// you can either add these two and compare it against the reference clock,
+// or you can call CBaseFilter::StreamTime and compare that against
+// the sample timestamp.
+
+STDMETHODIMP
+CBaseFilter::Run(REFERENCE_TIME tStart)
+{
+ CAutoLock cObjectLock(m_pLock);
+
+ // remember the stream time offset
+ m_tStart = tStart;
+
+ if (m_State == State_Stopped){
+ HRESULT hr = Pause();
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+ // notify all pins of the change to active state
+ if (m_State != State_Running) {
+ int cPins = GetPinCount();
+ for (int c = 0; c < cPins; c++) {
+
+ CBasePin *pPin = GetPin(c);
+ if (NULL == pPin) {
+ break;
+ }
+
+ // Disconnected pins are not activated - this saves pins
+ // worrying about this state themselves
+
+ if (pPin->IsConnected()) {
+ HRESULT hr = pPin->Run(tStart);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+ }
+ }
+
+#ifdef DXMPERF
+ PERFLOG_RUN( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, tStart, m_State );
+#endif // DXMPERF
+
+ m_State = State_Running;
+ return S_OK;
+}
+
+//
+// return the current stream time - samples with start timestamps of this
+// time or before should be rendered by now
+HRESULT
+CBaseFilter::StreamTime(CRefTime& rtStream)
+{
+ // Caller must lock for synchronization
+ // We can't grab the filter lock because we want to be able to call
+ // this from worker threads without deadlocking
+
+ if (m_pClock == NULL) {
+ return VFW_E_NO_CLOCK;
+ }
+
+ // get the current reference time
+ HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // subtract the stream offset to get stream time
+ rtStream -= m_tStart;
+
+ return S_OK;
+}
+
+
+/* Create an enumerator for the pins attached to this filter */
+
+STDMETHODIMP
+CBaseFilter::EnumPins(__deref_out IEnumPins **ppEnum)
+{
+ CheckPointer(ppEnum,E_POINTER);
+ ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));
+
+ /* Create a new ref counted enumerator */
+
+ *ppEnum = new CEnumPins(this,
+ NULL);
+
+ return *ppEnum == NULL ? E_OUTOFMEMORY : NOERROR;
+}
+
+
+// default behaviour of FindPin is to assume pins are named
+// by their pin names
+STDMETHODIMP
+CBaseFilter::FindPin(
+ LPCWSTR Id,
+ __deref_out IPin ** ppPin
+)
+{
+ CheckPointer(ppPin,E_POINTER);
+ ValidateReadWritePtr(ppPin,sizeof(IPin *));
+
+ // We're going to search the pin list so maintain integrity
+ CAutoLock lck(m_pLock);
+ int iCount = GetPinCount();
+ for (int i = 0; i < iCount; i++) {
+ CBasePin *pPin = GetPin(i);
+ if (NULL == pPin) {
+ break;
+ }
+
+ if (0 == lstrcmpW(pPin->Name(), Id)) {
+ // Found one that matches
+ //
+ // AddRef() and return it
+ *ppPin = pPin;
+ pPin->AddRef();
+ return S_OK;
+ }
+ }
+ *ppPin = NULL;
+ return VFW_E_NOT_FOUND;
+}
+
+/* Return information about this filter */
+
+STDMETHODIMP
+CBaseFilter::QueryFilterInfo(__out FILTER_INFO * pInfo)
+{
+ CheckPointer(pInfo,E_POINTER);
+ ValidateReadWritePtr(pInfo,sizeof(FILTER_INFO));
+
+ if (m_pName) {
+ (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);
+ } else {
+ pInfo->achName[0] = L'\0';
+ }
+ pInfo->pGraph = m_pGraph;
+ if (m_pGraph)
+ m_pGraph->AddRef();
+ return NOERROR;
+}
+
+
+/* Provide the filter with a filter graph */
+
+STDMETHODIMP
+CBaseFilter::JoinFilterGraph(
+ __inout_opt IFilterGraph * pGraph,
+ __in_opt LPCWSTR pName)
+{
+ CAutoLock cObjectLock(m_pLock);
+
+ // NOTE: we no longer hold references on the graph (m_pGraph, m_pSink)
+
+ m_pGraph = pGraph;
+ if (m_pGraph) {
+ HRESULT hr = m_pGraph->QueryInterface(IID_IMediaEventSink,
+ (void**) &m_pSink);
+ if (FAILED(hr)) {
+ ASSERT(m_pSink == NULL);
+ }
+ else m_pSink->Release(); // we do NOT keep a reference on it.
+ } else {
+ // if graph pointer is null, then we should
+ // also release the IMediaEventSink on the same object - we don't
+ // refcount it, so just set it to null
+ m_pSink = NULL;
+ }
+
+
+ if (m_pName) {
+ delete[] m_pName;
+ m_pName = NULL;
+ }
+
+ if (pName) {
+ size_t namelen;
+ HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &namelen);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ m_pName = new WCHAR[namelen + 1];
+ if (m_pName) {
+ (void)StringCchCopyW(m_pName, namelen + 1, pName);
+ } else {
+ return E_OUTOFMEMORY;
+ }
+ }
+
+#ifdef DXMPERF
+ PERFLOG_JOINGRAPH( m_pName ? m_pName : L"CBaseFilter",(IBaseFilter *) this, pGraph );
+#endif // DXMPERF
+
+ return NOERROR;
+}
+
+
+// return a Vendor information string. Optional - may return E_NOTIMPL.
+// memory returned should be freed using CoTaskMemFree
+// default implementation returns E_NOTIMPL
+STDMETHODIMP
+CBaseFilter::QueryVendorInfo(
+ __deref_out LPWSTR* pVendorInfo)
+{
+ UNREFERENCED_PARAMETER(pVendorInfo);
+ return E_NOTIMPL;
+}
+
+
+// send an event notification to the filter graph if we know about it.
+// returns S_OK if delivered, S_FALSE if the filter graph does not sink
+// events, or an error otherwise.
+HRESULT
+CBaseFilter::NotifyEvent(
+ long EventCode,
+ LONG_PTR EventParam1,
+ LONG_PTR EventParam2)
+{
+ // Snapshot so we don't have to lock up
+ IMediaEventSink *pSink = m_pSink;
+ if (pSink) {
+ if (EC_COMPLETE == EventCode) {
+ EventParam2 = (LONG_PTR)(IBaseFilter*)this;
+ }
+
+ return pSink->Notify(EventCode, EventParam1, EventParam2);
+ } else {
+ return E_NOTIMPL;
+ }
+}
+
+// Request reconnect
+// pPin is the pin to reconnect
+// pmt is the type to reconnect with - can be NULL
+// Calls ReconnectEx on the filter graph
+HRESULT
+CBaseFilter::ReconnectPin(
+ IPin *pPin,
+ __in_opt AM_MEDIA_TYPE const *pmt
+)
+{
+ IFilterGraph2 *pGraph2;
+ if (m_pGraph != NULL) {
+ HRESULT hr = m_pGraph->QueryInterface(IID_IFilterGraph2, (void **)&pGraph2);
+ if (SUCCEEDED(hr)) {
+ hr = pGraph2->ReconnectEx(pPin, pmt);
+ pGraph2->Release();
+ return hr;
+ } else {
+ return m_pGraph->Reconnect(pPin);
+ }
+ } else {
+ return E_NOINTERFACE;
+ }
+}
+
+
+
+/* This is the same idea as the media type version does for type enumeration
+ on pins but for the list of pins available. So if the list of pins you
+ provide changes dynamically then either override this virtual function
+ to provide the version number, or more simply call IncrementPinVersion */
+
+LONG CBaseFilter::GetPinVersion()
+{
+ return m_PinVersion;
+}
+
+
+/* Increment the current pin version cookie */
+
+void CBaseFilter::IncrementPinVersion()
+{
+ InterlockedIncrement(&m_PinVersion);
+}
+
+/* register filter */
+
+STDMETHODIMP CBaseFilter::Register()
+{
+ // get setup data, if it exists
+ //
+ LPAMOVIESETUP_FILTER psetupdata = GetSetupData();
+
+ // check we've got data
+ //
+ if( NULL == psetupdata ) return S_FALSE;
+
+ // init is ref counted so call just in case
+ // we're being called cold.
+ //
+ HRESULT hr = CoInitialize( (LPVOID)NULL );
+ ASSERT( SUCCEEDED(hr) );
+
+ // get hold of IFilterMapper
+ //
+ IFilterMapper *pIFM;
+ hr = CoCreateInstance( CLSID_FilterMapper
+ , NULL
+ , CLSCTX_INPROC_SERVER
+ , IID_IFilterMapper
+ , (void **)&pIFM );
+ if( SUCCEEDED(hr) )
+ {
+ hr = AMovieSetupRegisterFilter( psetupdata, pIFM, TRUE );
+ pIFM->Release();
+ }
+
+ // and clear up
+ //
+ CoFreeUnusedLibraries();
+ CoUninitialize();
+
+ return NOERROR;
+}
+
+
+/* unregister filter */
+
+STDMETHODIMP CBaseFilter::Unregister()
+{
+ // get setup data, if it exists
+ //
+ LPAMOVIESETUP_FILTER psetupdata = GetSetupData();
+
+ // check we've got data
+ //
+ if( NULL == psetupdata ) return S_FALSE;
+
+ // OLE init is ref counted so call
+ // just in case we're being called cold.
+ //
+ HRESULT hr = CoInitialize( (LPVOID)NULL );
+ ASSERT( SUCCEEDED(hr) );
+
+ // get hold of IFilterMapper
+ //
+ IFilterMapper *pIFM;
+ hr = CoCreateInstance( CLSID_FilterMapper
+ , NULL
+ , CLSCTX_INPROC_SERVER
+ , IID_IFilterMapper
+ , (void **)&pIFM );
+ if( SUCCEEDED(hr) )
+ {
+ hr = AMovieSetupRegisterFilter( psetupdata, pIFM, FALSE );
+
+ // release interface
+ //
+ pIFM->Release();
+ }
+
+ // clear up
+ //
+ CoFreeUnusedLibraries();
+ CoUninitialize();
+
+ // handle one acceptable "error" - that
+ // of filter not being registered!
+ // (couldn't find a suitable #define'd
+ // name for the error!)
+ //
+ if( 0x80070002 == hr)
+ return NOERROR;
+ else
+ return hr;
+}
+
+
+//=====================================================================
+//=====================================================================
+// Implements CEnumPins
+//=====================================================================
+//=====================================================================
+
+
+CEnumPins::CEnumPins(__in CBaseFilter *pFilter,
+ __in_opt CEnumPins *pEnumPins) :
+ m_Position(0),
+ m_PinCount(0),
+ m_pFilter(pFilter),
+ m_cRef(1), // Already ref counted
+ m_PinCache(NAME("Pin Cache"))
+{
+
+#ifdef DEBUG
+ m_dwCookie = DbgRegisterObjectCreation("CEnumPins", 0);
+#endif
+
+ /* We must be owned by a filter derived from CBaseFilter */
+
+ ASSERT(pFilter != NULL);
+
+ /* Hold a reference count on our filter */
+ m_pFilter->AddRef();
+
+ /* Are we creating a new enumerator */
+
+ if (pEnumPins == NULL) {
+ m_Version = m_pFilter->GetPinVersion();
+ m_PinCount = m_pFilter->GetPinCount();
+ } else {
+ ASSERT(m_Position <= m_PinCount);
+ m_Position = pEnumPins->m_Position;
+ m_PinCount = pEnumPins->m_PinCount;
+ m_Version = pEnumPins->m_Version;
+ m_PinCache.AddTail(&(pEnumPins->m_PinCache));
+ }
+}
+
+
+/* Destructor releases the reference count on our filter NOTE since we hold
+ a reference count on the filter who created us we know it is safe to
+ release it, no access can be made to it afterwards though as we have just
+ caused the last reference count to go and the object to be deleted */
+
+CEnumPins::~CEnumPins()
+{
+ m_pFilter->Release();
+
+#ifdef DEBUG
+ DbgRegisterObjectDestruction(m_dwCookie);
+#endif
+}
+
+
+/* Override this to say what interfaces we support where */
+
+STDMETHODIMP
+CEnumPins::QueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ CheckPointer(ppv, E_POINTER);
+
+ /* Do we have this interface */
+
+ if (riid == IID_IEnumPins || riid == IID_IUnknown) {
+ return GetInterface((IEnumPins *) this, ppv);
+ } else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+STDMETHODIMP_(ULONG)
+CEnumPins::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG)
+CEnumPins::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0) {
+ delete this;
+ }
+ return cRef;
+}
+
+/* One of an enumerator's basic member functions allows us to create a cloned
+ interface that initially has the same state. Since we are taking a snapshot
+ of an object (current position and all) we must lock access at the start */
+
+STDMETHODIMP
+CEnumPins::Clone(__deref_out IEnumPins **ppEnum)
+{
+ CheckPointer(ppEnum,E_POINTER);
+ ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));
+ HRESULT hr = NOERROR;
+
+ /* Check we are still in sync with the filter */
+ if (AreWeOutOfSync() == TRUE) {
+ *ppEnum = NULL;
+ hr = VFW_E_ENUM_OUT_OF_SYNC;
+ } else {
+ *ppEnum = new CEnumPins(m_pFilter,
+ this);
+ if (*ppEnum == NULL) {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+ return hr;
+}
+
+
+/* Return the next pin after the current position */
+
+STDMETHODIMP
+CEnumPins::Next(ULONG cPins, // place this many pins...
+ __out_ecount(cPins) IPin **ppPins, // ...in this array
+ __out_opt ULONG *pcFetched) // actual count passed returned here
+{
+ CheckPointer(ppPins,E_POINTER);
+ ValidateReadWritePtr(ppPins,cPins * sizeof(IPin *));
+
+ ASSERT(ppPins);
+
+ if (pcFetched!=NULL) {
+ ValidateWritePtr(pcFetched, sizeof(ULONG));
+ *pcFetched = 0; // default unless we succeed
+ }
+ // now check that the parameter is valid
+ else if (cPins>1) { // pcFetched == NULL
+ return E_INVALIDARG;
+ }
+ ULONG cFetched = 0; // increment as we get each one.
+
+ /* Check we are still in sync with the filter */
+ if (AreWeOutOfSync() == TRUE) {
+ // If we are out of sync, we should refresh the enumerator.
+ // This will reset the position and update the other members, but
+ // will not clear cache of pins we have already returned.
+ Refresh();
+ }
+
+ /* Return each pin interface NOTE GetPin returns CBasePin * not addrefed
+ so we must QI for the IPin (which increments its reference count)
+ If while we are retrieving a pin from the filter an error occurs we
+ assume that our internal state is stale with respect to the filter
+ (for example someone has deleted a pin) so we
+ return VFW_E_ENUM_OUT_OF_SYNC */
+
+ while (cFetched < cPins && m_PinCount > m_Position) {
+
+ /* Get the next pin object from the filter */
+
+ CBasePin *pPin = m_pFilter->GetPin(m_Position++);
+ if (pPin == NULL) {
+ // If this happend, and it's not the first time through, then we've got a problem,
+ // since we should really go back and release the iPins, which we have previously
+ // AddRef'ed.
+ ASSERT( cFetched==0 );
+ return VFW_E_ENUM_OUT_OF_SYNC;
+ }
+
+ /* We only want to return this pin, if it is not in our cache */
+ if (0 == m_PinCache.Find(pPin))
+ {
+ /* From the object get an IPin interface */
+
+ *ppPins = pPin;
+ pPin->AddRef();
+
+ cFetched++;
+ ppPins++;
+
+ m_PinCache.AddTail(pPin);
+ }
+ }
+
+ if (pcFetched!=NULL) {
+ *pcFetched = cFetched;
+ }
+
+ return (cPins==cFetched ? NOERROR : S_FALSE);
+}
+
+
+/* Skip over one or more entries in the enumerator */
+
+STDMETHODIMP
+CEnumPins::Skip(ULONG cPins)
+{
+ /* Check we are still in sync with the filter */
+ if (AreWeOutOfSync() == TRUE) {
+ return VFW_E_ENUM_OUT_OF_SYNC;
+ }
+
+ /* Work out how many pins are left to skip over */
+ /* We could position at the end if we are asked to skip too many... */
+ /* ..which would match the base implementation for CEnumMediaTypes::Skip */
+
+ ULONG PinsLeft = m_PinCount - m_Position;
+ if (cPins > PinsLeft) {
+ return S_FALSE;
+ }
+ m_Position += cPins;
+ return NOERROR;
+}
+
+
+/* Set the current position back to the start */
+/* Reset has 4 simple steps:
+ *
+ * Set position to head of list
+ * Sync enumerator with object being enumerated
+ * Clear the cache of pins already returned
+ * return S_OK
+ */
+
+STDMETHODIMP
+CEnumPins::Reset()
+{
+ m_Version = m_pFilter->GetPinVersion();
+ m_PinCount = m_pFilter->GetPinCount();
+
+ m_Position = 0;
+
+ // Clear the cache
+ m_PinCache.RemoveAll();
+
+ return S_OK;
+}
+
+
+/* Set the current position back to the start */
+/* Refresh has 3 simple steps:
+ *
+ * Set position to head of list
+ * Sync enumerator with object being enumerated
+ * return S_OK
+ */
+
+STDMETHODIMP
+CEnumPins::Refresh()
+{
+ m_Version = m_pFilter->GetPinVersion();
+ m_PinCount = m_pFilter->GetPinCount();
+
+ m_Position = 0;
+ return S_OK;
+}
+
+
+//=====================================================================
+//=====================================================================
+// Implements CEnumMediaTypes
+//=====================================================================
+//=====================================================================
+
+
+CEnumMediaTypes::CEnumMediaTypes(__in CBasePin *pPin,
+ __in_opt CEnumMediaTypes *pEnumMediaTypes) :
+ m_Position(0),
+ m_pPin(pPin),
+ m_cRef(1)
+{
+
+#ifdef DEBUG
+ m_dwCookie = DbgRegisterObjectCreation("CEnumMediaTypes", 0);
+#endif
+
+ /* We must be owned by a pin derived from CBasePin */
+
+ ASSERT(pPin != NULL);
+
+ /* Hold a reference count on our pin */
+ m_pPin->AddRef();
+
+ /* Are we creating a new enumerator */
+
+ if (pEnumMediaTypes == NULL) {
+ m_Version = m_pPin->GetMediaTypeVersion();
+ return;
+ }
+
+ m_Position = pEnumMediaTypes->m_Position;
+ m_Version = pEnumMediaTypes->m_Version;
+}
+
+
+/* Destructor releases the reference count on our base pin. NOTE since we hold
+ a reference count on the pin who created us we know it is safe to release
+ it, no access can be made to it afterwards though as we might have just
+ caused the last reference count to go and the object to be deleted */
+
+CEnumMediaTypes::~CEnumMediaTypes()
+{
+#ifdef DEBUG
+ DbgRegisterObjectDestruction(m_dwCookie);
+#endif
+ m_pPin->Release();
+}
+
+
+/* Override this to say what interfaces we support where */
+
+STDMETHODIMP
+CEnumMediaTypes::QueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ CheckPointer(ppv, E_POINTER);
+
+ /* Do we have this interface */
+
+ if (riid == IID_IEnumMediaTypes || riid == IID_IUnknown) {
+ return GetInterface((IEnumMediaTypes *) this, ppv);
+ } else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+STDMETHODIMP_(ULONG)
+CEnumMediaTypes::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG)
+CEnumMediaTypes::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0) {
+ delete this;
+ }
+ return cRef;
+}
+
+/* One of an enumerator's basic member functions allows us to create a cloned
+ interface that initially has the same state. Since we are taking a snapshot
+ of an object (current position and all) we must lock access at the start */
+
+STDMETHODIMP
+CEnumMediaTypes::Clone(__deref_out IEnumMediaTypes **ppEnum)
+{
+ CheckPointer(ppEnum,E_POINTER);
+ ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));
+ HRESULT hr = NOERROR;
+
+ /* Check we are still in sync with the pin */
+ if (AreWeOutOfSync() == TRUE) {
+ *ppEnum = NULL;
+ hr = VFW_E_ENUM_OUT_OF_SYNC;
+ } else {
+
+ *ppEnum = new CEnumMediaTypes(m_pPin,
+ this);
+
+ if (*ppEnum == NULL) {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+ return hr;
+}
+
+
+/* Enumerate the next pin(s) after the current position. The client using this
+ interface passes in a pointer to an array of pointers each of which will
+ be filled in with a pointer to a fully initialised media type format
+ Return NOERROR if it all works,
+ S_FALSE if fewer than cMediaTypes were enumerated.
+ VFW_E_ENUM_OUT_OF_SYNC if the enumerator has been broken by
+ state changes in the filter
+ The actual count always correctly reflects the number of types in the array.
+*/
+
+STDMETHODIMP
+CEnumMediaTypes::Next(ULONG cMediaTypes, // place this many types...
+ __out_ecount(cMediaTypes) AM_MEDIA_TYPE **ppMediaTypes, // ...in this array
+ __out ULONG *pcFetched) // actual count passed
+{
+ CheckPointer(ppMediaTypes,E_POINTER);
+ ValidateReadWritePtr(ppMediaTypes,cMediaTypes * sizeof(AM_MEDIA_TYPE *));
+ /* Check we are still in sync with the pin */
+ if (AreWeOutOfSync() == TRUE) {
+ return VFW_E_ENUM_OUT_OF_SYNC;
+ }
+
+ if (pcFetched!=NULL) {
+ ValidateWritePtr(pcFetched, sizeof(ULONG));
+ *pcFetched = 0; // default unless we succeed
+ }
+ // now check that the parameter is valid
+ else if (cMediaTypes>1) { // pcFetched == NULL
+ return E_INVALIDARG;
+ }
+ ULONG cFetched = 0; // increment as we get each one.
+
+ /* Return each media type by asking the filter for them in turn - If we
+ have an error code retured to us while we are retrieving a media type
+ we assume that our internal state is stale with respect to the filter
+ (for example the window size changing) so we return
+ VFW_E_ENUM_OUT_OF_SYNC */
+
+ while (cMediaTypes) {
+
+ CMediaType cmt;
+
+ HRESULT hr = m_pPin->GetMediaType(m_Position++, &cmt);
+ if (S_OK != hr) {
+ break;
+ }
+
+ /* We now have a CMediaType object that contains the next media type
+ but when we assign it to the array position we CANNOT just assign
+ the AM_MEDIA_TYPE structure because as soon as the object goes out of
+ scope it will delete the memory we have just copied. The function
+ we use is CreateMediaType which allocates a task memory block */
+
+ /* Transfer across the format block manually to save an allocate
+ and free on the format block and generally go faster */
+
+ *ppMediaTypes = (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
+ if (*ppMediaTypes == NULL) {
+ break;
+ }
+
+ /* Do a regular copy */
+ **ppMediaTypes = cmt;
+
+ /* Make sure the destructor doesn't free these */
+ cmt.pbFormat = NULL;
+ cmt.cbFormat = NULL;
+ cmt.pUnk = NULL;
+
+
+ ppMediaTypes++;
+ cFetched++;
+ cMediaTypes--;
+ }
+
+ if (pcFetched!=NULL) {
+ *pcFetched = cFetched;
+ }
+
+ return ( cMediaTypes==0 ? NOERROR : S_FALSE );
+}
+
+
+/* Skip over one or more entries in the enumerator */
+
+STDMETHODIMP
+CEnumMediaTypes::Skip(ULONG cMediaTypes)
+{
+ // If we're skipping 0 elements we're guaranteed to skip the
+ // correct number of elements
+ if (cMediaTypes == 0) {
+ return S_OK;
+ }
+
+ /* Check we are still in sync with the pin */
+ if (AreWeOutOfSync() == TRUE) {
+ return VFW_E_ENUM_OUT_OF_SYNC;
+ }
+
+ m_Position += cMediaTypes;
+
+ /* See if we're over the end */
+ CMediaType cmt;
+ return S_OK == m_pPin->GetMediaType(m_Position - 1, &cmt) ? S_OK : S_FALSE;
+}
+
+
+/* Set the current position back to the start */
+/* Reset has 3 simple steps:
+ *
+ * set position to head of list
+ * sync enumerator with object being enumerated
+ * return S_OK
+ */
+
+STDMETHODIMP
+CEnumMediaTypes::Reset()
+
+{
+ m_Position = 0;
+
+ // Bring the enumerator back into step with the current state. This
+ // may be a noop but ensures that the enumerator will be valid on the
+ // next call.
+ m_Version = m_pPin->GetMediaTypeVersion();
+ return NOERROR;
+}
+
+
+//=====================================================================
+//=====================================================================
+// Implements CBasePin
+//=====================================================================
+//=====================================================================
+
+
+/* NOTE The implementation of this class calls the CUnknown constructor with
+ a NULL outer unknown pointer. This has the effect of making us a self
+ contained class, ie any QueryInterface, AddRef or Release calls will be
+ routed to the class's NonDelegatingUnknown methods. You will typically
+ find that the classes that do this then override one or more of these
+ virtual functions to provide more specialised behaviour. A good example
+ of this is where a class wants to keep the QueryInterface internal but
+ still wants its lifetime controlled by the external object */
+
+/* Constructor */
+
+CBasePin::CBasePin(__in_opt LPCTSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName,
+ PIN_DIRECTION dir) :
+ CUnknown( pObjectName, NULL ),
+ m_pFilter(pFilter),
+ m_pLock(pLock),
+ m_pName(NULL),
+ m_Connected(NULL),
+ m_dir(dir),
+ m_bRunTimeError(FALSE),
+ m_pQSink(NULL),
+ m_TypeVersion(1),
+ m_tStart(),
+ m_tStop(MAX_TIME),
+ m_bCanReconnectWhenActive(false),
+ m_bTryMyTypesFirst(false),
+ m_dRate(1.0)
+{
+ /* WARNING - pFilter is often not a properly constituted object at
+ this state (in particular QueryInterface may not work) - this
+ is because its owner is often its containing object and we
+ have been called from the containing object's constructor so
+ the filter's owner has not yet had its CUnknown constructor
+ called
+ */
+#ifdef DXMPERF
+ PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );
+#endif // DXMPERF
+
+ ASSERT(pFilter != NULL);
+ ASSERT(pLock != NULL);
+
+ if (pName) {
+ size_t cchName;
+ HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);
+ if (SUCCEEDED(hr)) {
+ m_pName = new WCHAR[cchName + 1];
+ if (m_pName) {
+ (void)StringCchCopyW(m_pName, cchName + 1, pName);
+ }
+ }
+ }
+
+#ifdef DEBUG
+ m_cRef = 0;
+#endif
+}
+
+#ifdef UNICODE
+CBasePin::CBasePin(__in_opt LPCSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName,
+ PIN_DIRECTION dir) :
+ CUnknown( pObjectName, NULL ),
+ m_pFilter(pFilter),
+ m_pLock(pLock),
+ m_pName(NULL),
+ m_Connected(NULL),
+ m_dir(dir),
+ m_bRunTimeError(FALSE),
+ m_pQSink(NULL),
+ m_TypeVersion(1),
+ m_tStart(),
+ m_tStop(MAX_TIME),
+ m_bCanReconnectWhenActive(false),
+ m_bTryMyTypesFirst(false),
+ m_dRate(1.0)
+{
+ /* WARNING - pFilter is often not a properly constituted object at
+ this state (in particular QueryInterface may not work) - this
+ is because its owner is often its containing object and we
+ have been called from the containing object's constructor so
+ the filter's owner has not yet had its CUnknown constructor
+ called
+ */
+#ifdef DXMPERF
+ PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );
+#endif // DXMPERF
+
+ ASSERT(pFilter != NULL);
+ ASSERT(pLock != NULL);
+
+ if (pName) {
+ size_t cchName;
+ HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);
+ if (SUCCEEDED(hr)) {
+ m_pName = new WCHAR[cchName + 1];
+ if (m_pName) {
+ (void)StringCchCopyW(m_pName, cchName + 1, pName);
+ }
+ }
+ }
+
+
+#ifdef DEBUG
+ m_cRef = 0;
+#endif
+}
+#endif
+
+/* Destructor since a connected pin holds a reference count on us there is
+ no way that we can be deleted unless we are not currently connected */
+
+CBasePin::~CBasePin()
+{
+#ifdef DXMPERF
+ PERFLOG_DTOR( m_pName ? m_pName : L"CBasePin", (IPin *) this );
+#endif // DXMPERF
+
+ // We don't call disconnect because if the filter is going away
+ // all the pins must have a reference count of zero so they must
+ // have been disconnected anyway - (but check the assumption)
+ ASSERT(m_Connected == FALSE);
+
+ delete[] m_pName;
+
+ // check the internal reference count is consistent
+ ASSERT(m_cRef == 0);
+}
+
+
+/* Override this to say what interfaces we support and where */
+
+STDMETHODIMP
+CBasePin::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)
+{
+ /* Do we have this interface */
+
+ if (riid == IID_IPin) {
+ return GetInterface((IPin *) this, ppv);
+ } else if (riid == IID_IQualityControl) {
+ return GetInterface((IQualityControl *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+/* Override to increment the owning filter's reference count */
+
+STDMETHODIMP_(ULONG)
+CBasePin::NonDelegatingAddRef()
+{
+ ASSERT(InterlockedIncrement(&m_cRef) > 0);
+ return m_pFilter->AddRef();
+}
+
+
+/* Override to decrement the owning filter's reference count */
+
+STDMETHODIMP_(ULONG)
+CBasePin::NonDelegatingRelease()
+{
+ ASSERT(InterlockedDecrement(&m_cRef) >= 0);
+ return m_pFilter->Release();
+}
+
+
+/* Displays pin connection information */
+
+#ifdef DEBUG
+void
+CBasePin::DisplayPinInfo(IPin *pReceivePin)
+{
+
+ if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {
+ PIN_INFO ConnectPinInfo;
+ PIN_INFO ReceivePinInfo;
+
+ if (FAILED(QueryPinInfo(&ConnectPinInfo))) {
+ StringCchCopyW(ConnectPinInfo.achName, sizeof(ConnectPinInfo.achName)/sizeof(WCHAR), L"Bad Pin");
+ } else {
+ QueryPinInfoReleaseFilter(ConnectPinInfo);
+ }
+
+ if (FAILED(pReceivePin->QueryPinInfo(&ReceivePinInfo))) {
+ StringCchCopyW(ReceivePinInfo.achName, sizeof(ReceivePinInfo.achName)/sizeof(WCHAR), L"Bad Pin");
+ } else {
+ QueryPinInfoReleaseFilter(ReceivePinInfo);
+ }
+
+ DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying to connect Pins :")));
+ DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" <%ls>"), ConnectPinInfo.achName));
+ DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" <%ls>"), ReceivePinInfo.achName));
+ }
+}
+#endif
+
+
+/* Displays general information on the pin media type */
+
+#ifdef DEBUG
+void CBasePin::DisplayTypeInfo(IPin *pPin, const CMediaType *pmt)
+{
+ UNREFERENCED_PARAMETER(pPin);
+ if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {
+ DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying media type:")));
+ DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" major type: %hs"),
+ GuidNames[*pmt->Type()]));
+ DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" sub type : %hs"),
+ GuidNames[*pmt->Subtype()]));
+ }
+}
+#endif
+
+/* Asked to connect to a pin. A pin is always attached to an owning filter
+ object so we always delegate our locking to that object. We first of all
+ retrieve a media type enumerator for the input pin and see if we accept
+ any of the formats that it would ideally like, failing that we retrieve
+ our enumerator and see if it will accept any of our preferred types */
+
+STDMETHODIMP
+CBasePin::Connect(
+ IPin * pReceivePin,
+ __in_opt const AM_MEDIA_TYPE *pmt // optional media type
+)
+{
+ CheckPointer(pReceivePin,E_POINTER);
+ ValidateReadPtr(pReceivePin,sizeof(IPin));
+ CAutoLock cObjectLock(m_pLock);
+ DisplayPinInfo(pReceivePin);
+
+ /* See if we are already connected */
+
+ if (m_Connected) {
+ DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Already connected")));
+ return VFW_E_ALREADY_CONNECTED;
+ }
+
+ /* See if the filter is active */
+ if (!IsStopped() && !m_bCanReconnectWhenActive) {
+ return VFW_E_NOT_STOPPED;
+ }
+
+
+ // Find a mutually agreeable media type -
+ // Pass in the template media type. If this is partially specified,
+ // each of the enumerated media types will need to be checked against
+ // it. If it is non-null and fully specified, we will just try to connect
+ // with this.
+
+ const CMediaType * ptype = (CMediaType*)pmt;
+ HRESULT hr = AgreeMediaType(pReceivePin, ptype);
+ if (FAILED(hr)) {
+ DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to agree type")));
+
+ // Since the procedure is already returning an error code, there
+ // is nothing else this function can do to report the error.
+ EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
+
+#ifdef DXMPERF
+ PERFLOG_CONNECT( (IPin *) this, pReceivePin, hr, pmt );
+#endif // DXMPERF
+
+ return hr;
+ }
+
+ DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Connection succeeded")));
+
+#ifdef DXMPERF
+ PERFLOG_CONNECT( (IPin *) this, pReceivePin, NOERROR, pmt );
+#endif // DXMPERF
+
+ return NOERROR;
+}
+
+// given a specific media type, attempt a connection (includes
+// checking that the type is acceptable to this pin)
+HRESULT
+CBasePin::AttemptConnection(
+ IPin* pReceivePin, // connect to this pin
+ const CMediaType* pmt // using this type
+)
+{
+ // The caller should hold the filter lock becasue this function
+ // uses m_Connected. The caller should also hold the filter lock
+ // because this function calls SetMediaType(), IsStopped() and
+ // CompleteConnect().
+ ASSERT(CritCheckIn(m_pLock));
+
+ // Check that the connection is valid -- need to do this for every
+ // connect attempt since BreakConnect will undo it.
+ HRESULT hr = CheckConnect(pReceivePin);
+ if (FAILED(hr)) {
+ DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("CheckConnect failed")));
+
+ // Since the procedure is already returning an error code, there
+ // is nothing else this function can do to report the error.
+ EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
+
+ return hr;
+ }
+
+ DisplayTypeInfo(pReceivePin, pmt);
+
+ /* Check we will accept this media type */
+
+ hr = CheckMediaType(pmt);
+ if (hr == NOERROR) {
+
+ /* Make ourselves look connected otherwise ReceiveConnection
+ may not be able to complete the connection
+ */
+ m_Connected = pReceivePin;
+ m_Connected->AddRef();
+ hr = SetMediaType(pmt);
+ if (SUCCEEDED(hr)) {
+ /* See if the other pin will accept this type */
+
+ hr = pReceivePin->ReceiveConnection((IPin *)this, pmt);
+ if (SUCCEEDED(hr)) {
+ /* Complete the connection */
+
+ hr = CompleteConnect(pReceivePin);
+ if (SUCCEEDED(hr)) {
+ return hr;
+ } else {
+ DbgLog((LOG_TRACE,
+ CONNECT_TRACE_LEVEL,
+ TEXT("Failed to complete connection")));
+ pReceivePin->Disconnect();
+ }
+ }
+ }
+ } else {
+ // we cannot use this media type
+
+ // return a specific media type error if there is one
+ // or map a general failure code to something more helpful
+ // (in particular S_FALSE gets changed to an error code)
+ if (SUCCEEDED(hr) ||
+ (hr == E_FAIL) ||
+ (hr == E_INVALIDARG)) {
+ hr = VFW_E_TYPE_NOT_ACCEPTED;
+ }
+ }
+
+ // BreakConnect and release any connection here in case CheckMediaType
+ // failed, or if we set anything up during a call back during
+ // ReceiveConnection.
+
+ // Since the procedure is already returning an error code, there
+ // is nothing else this function can do to report the error.
+ EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
+
+ /* If failed then undo our state */
+ if (m_Connected) {
+ m_Connected->Release();
+ m_Connected = NULL;
+ }
+
+ return hr;
+}
+
+/* Given an enumerator we cycle through all the media types it proposes and
+ firstly suggest them to our derived pin class and if that succeeds try
+ them with the pin in a ReceiveConnection call. This means that if our pin
+ proposes a media type we still check in here that we can support it. This
+ is deliberate so that in simple cases the enumerator can hold all of the
+ media types even if some of them are not really currently available */
+
+HRESULT CBasePin::TryMediaTypes(
+ IPin *pReceivePin,
+ __in_opt const CMediaType *pmt,
+ IEnumMediaTypes *pEnum)
+{
+ /* Reset the current enumerator position */
+
+ HRESULT hr = pEnum->Reset();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ CMediaType *pMediaType = NULL;
+ ULONG ulMediaCount = 0;
+
+ // attempt to remember a specific error code if there is one
+ HRESULT hrFailure = S_OK;
+
+ for (;;) {
+
+ /* Retrieve the next media type NOTE each time round the loop the
+ enumerator interface will allocate another AM_MEDIA_TYPE structure
+ If we are successful then we copy it into our output object, if
+ not then we must delete the memory allocated before returning */
+
+ hr = pEnum->Next(1, (AM_MEDIA_TYPE**)&pMediaType,&ulMediaCount);
+ if (hr != S_OK) {
+ if (S_OK == hrFailure) {
+ hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;
+ }
+ return hrFailure;
+ }
+
+
+ ASSERT(ulMediaCount == 1);
+ ASSERT(pMediaType);
+
+ // check that this matches the partial type (if any)
+
+ if (pMediaType &&
+ ((pmt == NULL) ||
+ pMediaType->MatchesPartial(pmt))) {
+
+ hr = AttemptConnection(pReceivePin, pMediaType);
+
+ // attempt to remember a specific error code
+ if (FAILED(hr) &&
+ SUCCEEDED(hrFailure) &&
+ (hr != E_FAIL) &&
+ (hr != E_INVALIDARG) &&
+ (hr != VFW_E_TYPE_NOT_ACCEPTED)) {
+ hrFailure = hr;
+ }
+ } else {
+ hr = VFW_E_NO_ACCEPTABLE_TYPES;
+ }
+
+ if(pMediaType) {
+ DeleteMediaType(pMediaType);
+ pMediaType = NULL;
+ }
+
+ if (S_OK == hr) {
+ return hr;
+ }
+ }
+}
+
+
+/* This is called to make the connection, including the taask of finding
+ a media type for the pin connection. pmt is the proposed media type
+ from the Connect call: if this is fully specified, we will try that.
+ Otherwise we enumerate and try all the input pin's types first and
+ if that fails we then enumerate and try all our preferred media types.
+ For each media type we check it against pmt (if non-null and partially
+ specified) as well as checking that both pins will accept it.
+ */
+
+HRESULT CBasePin::AgreeMediaType(
+ IPin *pReceivePin,
+ const CMediaType *pmt)
+{
+ ASSERT(pReceivePin);
+ IEnumMediaTypes *pEnumMediaTypes = NULL;
+
+ // if the media type is fully specified then use that
+ if ( (pmt != NULL) && (!pmt->IsPartiallySpecified())) {
+
+ // if this media type fails, then we must fail the connection
+ // since if pmt is nonnull we are only allowed to connect
+ // using a type that matches it.
+
+ return AttemptConnection(pReceivePin, pmt);
+ }
+
+
+ /* Try the other pin's enumerator */
+
+ HRESULT hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;
+
+ for (int i = 0; i < 2; i++) {
+ HRESULT hr;
+ if (i == (int)m_bTryMyTypesFirst) {
+ hr = pReceivePin->EnumMediaTypes(&pEnumMediaTypes);
+ } else {
+ hr = EnumMediaTypes(&pEnumMediaTypes);
+ }
+ if (SUCCEEDED(hr)) {
+ ASSERT(pEnumMediaTypes);
+ hr = TryMediaTypes(pReceivePin,pmt,pEnumMediaTypes);
+ pEnumMediaTypes->Release();
+ if (SUCCEEDED(hr)) {
+ return NOERROR;
+ } else {
+ // try to remember specific error codes if there are any
+ if ((hr != E_FAIL) &&
+ (hr != E_INVALIDARG) &&
+ (hr != VFW_E_TYPE_NOT_ACCEPTED)) {
+ hrFailure = hr;
+ }
+ }
+ }
+ }
+
+ return hrFailure;
+}
+
+
+/* Called when we want to complete a connection to another filter. Failing
+ this will also fail the connection and disconnect the other pin as well */
+
+HRESULT
+CBasePin::CompleteConnect(IPin *pReceivePin)
+{
+ UNREFERENCED_PARAMETER(pReceivePin);
+ return NOERROR;
+}
+
+
+/* This is called to set the format for a pin connection - CheckMediaType
+ will have been called to check the connection format and if it didn't
+ return an error code then this (virtual) function will be invoked */
+
+HRESULT
+CBasePin::SetMediaType(const CMediaType *pmt)
+{
+ HRESULT hr = m_mt.Set(*pmt);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return NOERROR;
+}
+
+
+/* This is called during Connect() to provide a virtual method that can do
+ any specific check needed for connection such as QueryInterface. This
+ base class method just checks that the pin directions don't match */
+
+HRESULT
+CBasePin::CheckConnect(IPin * pPin)
+{
+ /* Check that pin directions DONT match */
+
+ PIN_DIRECTION pd;
+ pPin->QueryDirection(&pd);
+
+ ASSERT((pd == PINDIR_OUTPUT) || (pd == PINDIR_INPUT));
+ ASSERT((m_dir == PINDIR_OUTPUT) || (m_dir == PINDIR_INPUT));
+
+ // we should allow for non-input and non-output connections?
+ if (pd == m_dir) {
+ return VFW_E_INVALID_DIRECTION;
+ }
+ return NOERROR;
+}
+
+
+/* This is called when we realise we can't make a connection to the pin and
+ must undo anything we did in CheckConnect - override to release QIs done */
+
+HRESULT
+CBasePin::BreakConnect()
+{
+ return NOERROR;
+}
+
+
+/* Called normally by an output pin on an input pin to try and establish a
+ connection.
+*/
+
+STDMETHODIMP
+CBasePin::ReceiveConnection(
+ IPin * pConnector, // this is the pin who we will connect to
+ const AM_MEDIA_TYPE *pmt // this is the media type we will exchange
+)
+{
+ CheckPointer(pConnector,E_POINTER);
+ CheckPointer(pmt,E_POINTER);
+ ValidateReadPtr(pConnector,sizeof(IPin));
+ ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));
+ CAutoLock cObjectLock(m_pLock);
+
+ /* Are we already connected */
+ if (m_Connected) {
+ return VFW_E_ALREADY_CONNECTED;
+ }
+
+ /* See if the filter is active */
+ if (!IsStopped() && !m_bCanReconnectWhenActive) {
+ return VFW_E_NOT_STOPPED;
+ }
+
+ HRESULT hr = CheckConnect(pConnector);
+ if (FAILED(hr)) {
+ // Since the procedure is already returning an error code, there
+ // is nothing else this function can do to report the error.
+ EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
+
+#ifdef DXMPERF
+ PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
+#endif // DXMPERF
+
+ return hr;
+ }
+
+ /* Ask derived class if this media type is ok */
+
+ CMediaType * pcmt = (CMediaType*) pmt;
+ hr = CheckMediaType(pcmt);
+ if (hr != NOERROR) {
+ // no -we don't support this media type
+
+ // Since the procedure is already returning an error code, there
+ // is nothing else this function can do to report the error.
+ EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
+
+ // return a specific media type error if there is one
+ // or map a general failure code to something more helpful
+ // (in particular S_FALSE gets changed to an error code)
+ if (SUCCEEDED(hr) ||
+ (hr == E_FAIL) ||
+ (hr == E_INVALIDARG)) {
+ hr = VFW_E_TYPE_NOT_ACCEPTED;
+ }
+
+#ifdef DXMPERF
+ PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
+#endif // DXMPERF
+
+ return hr;
+ }
+
+ /* Complete the connection */
+
+ m_Connected = pConnector;
+ m_Connected->AddRef();
+ hr = SetMediaType(pcmt);
+ if (SUCCEEDED(hr)) {
+ hr = CompleteConnect(pConnector);
+ if (SUCCEEDED(hr)) {
+
+#ifdef DXMPERF
+ PERFLOG_RXCONNECT( pConnector, (IPin *) this, NOERROR, pmt );
+#endif // DXMPERF
+
+ return NOERROR;
+ }
+ }
+
+ DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to set the media type or failed to complete the connection.")));
+ m_Connected->Release();
+ m_Connected = NULL;
+
+ // Since the procedure is already returning an error code, there
+ // is nothing else this function can do to report the error.
+ EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
+
+#ifdef DXMPERF
+ PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
+#endif // DXMPERF
+
+ return hr;
+}
+
+
+/* Called when we want to terminate a pin connection */
+
+STDMETHODIMP
+CBasePin::Disconnect()
+{
+ CAutoLock cObjectLock(m_pLock);
+
+ /* See if the filter is active */
+ if (!IsStopped()) {
+ return VFW_E_NOT_STOPPED;
+ }
+
+ return DisconnectInternal();
+}
+
+STDMETHODIMP
+CBasePin::DisconnectInternal()
+{
+ ASSERT(CritCheckIn(m_pLock));
+
+ if (m_Connected) {
+ HRESULT hr = BreakConnect();
+ if( FAILED( hr ) ) {
+
+#ifdef DXMPERF
+ PERFLOG_DISCONNECT( (IPin *) this, m_Connected, hr );
+#endif // DXMPERF
+
+ // There is usually a bug in the program if BreakConnect() fails.
+ DbgBreak( "WARNING: BreakConnect() failed in CBasePin::Disconnect()." );
+ return hr;
+ }
+
+ m_Connected->Release();
+ m_Connected = NULL;
+
+#ifdef DXMPERF
+ PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_OK );
+#endif // DXMPERF
+
+ return S_OK;
+ } else {
+ // no connection - not an error
+
+#ifdef DXMPERF
+ PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_FALSE );
+#endif // DXMPERF
+
+ return S_FALSE;
+ }
+}
+
+
+/* Return an AddRef()'d pointer to the connected pin if there is one */
+STDMETHODIMP
+CBasePin::ConnectedTo(
+ __deref_out IPin **ppPin
+)
+{
+ CheckPointer(ppPin,E_POINTER);
+ ValidateReadWritePtr(ppPin,sizeof(IPin *));
+ //
+ // It's pointless to lock here.
+ // The caller should ensure integrity.
+ //
+
+ IPin *pPin = m_Connected;
+ *ppPin = pPin;
+ if (pPin != NULL) {
+ pPin->AddRef();
+ return S_OK;
+ } else {
+ ASSERT(*ppPin == NULL);
+ return VFW_E_NOT_CONNECTED;
+ }
+}
+
+/* Return the media type of the connection */
+STDMETHODIMP
+CBasePin::ConnectionMediaType(
+ __out AM_MEDIA_TYPE *pmt
+)
+{
+ CheckPointer(pmt,E_POINTER);
+ ValidateReadWritePtr(pmt,sizeof(AM_MEDIA_TYPE));
+ CAutoLock cObjectLock(m_pLock);
+
+ /* Copy constructor of m_mt allocates the memory */
+ if (IsConnected()) {
+ CopyMediaType( pmt, &m_mt );
+ return S_OK;
+ } else {
+ ((CMediaType *)pmt)->InitMediaType();
+ return VFW_E_NOT_CONNECTED;
+ }
+}
+
+/* Return information about the filter we are connect to */
+
+STDMETHODIMP
+CBasePin::QueryPinInfo(
+ __out PIN_INFO * pInfo
+)
+{
+ CheckPointer(pInfo,E_POINTER);
+ ValidateReadWritePtr(pInfo,sizeof(PIN_INFO));
+
+ pInfo->pFilter = m_pFilter;
+ if (m_pFilter) {
+ m_pFilter->AddRef();
+ }
+
+ if (m_pName) {
+ (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);
+ } else {
+ pInfo->achName[0] = L'\0';
+ }
+
+ pInfo->dir = m_dir;
+
+ return NOERROR;
+}
+
+STDMETHODIMP
+CBasePin::QueryDirection(
+ __out PIN_DIRECTION * pPinDir
+)
+{
+ CheckPointer(pPinDir,E_POINTER);
+ ValidateReadWritePtr(pPinDir,sizeof(PIN_DIRECTION));
+
+ *pPinDir = m_dir;
+ return NOERROR;
+}
+
+// Default QueryId to return the pin's name
+STDMETHODIMP
+CBasePin::QueryId(
+ __deref_out LPWSTR * Id
+)
+{
+ // We're not going away because someone's got a pointer to us
+ // so there's no need to lock
+
+ return AMGetWideString(Name(), Id);
+}
+
+/* Does this pin support this media type WARNING this interface function does
+ not lock the main object as it is meant to be asynchronous by nature - if
+ the media types you support depend on some internal state that is updated
+ dynamically then you will need to implement locking in a derived class */
+
+STDMETHODIMP
+CBasePin::QueryAccept(
+ const AM_MEDIA_TYPE *pmt
+)
+{
+ CheckPointer(pmt,E_POINTER);
+ ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));
+
+ /* The CheckMediaType method is valid to return error codes if the media
+ type is horrible, an example might be E_INVALIDARG. What we do here
+ is map all the error codes into either S_OK or S_FALSE regardless */
+
+ HRESULT hr = CheckMediaType((CMediaType*)pmt);
+ if (FAILED(hr)) {
+ return S_FALSE;
+ }
+ // note that the only defined success codes should be S_OK and S_FALSE...
+ return hr;
+}
+
+
+/* This can be called to return an enumerator for the pin's list of preferred
+ media types. An input pin is not obliged to have any preferred formats
+ although it can do. For example, the window renderer has a preferred type
+ which describes a video image that matches the current window size. All
+ output pins should expose at least one preferred format otherwise it is
+ possible that neither pin has any types and so no connection is possible */
+
+STDMETHODIMP
+CBasePin::EnumMediaTypes(
+ __deref_out IEnumMediaTypes **ppEnum
+)
+{
+ CheckPointer(ppEnum,E_POINTER);
+ ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));
+
+ /* Create a new ref counted enumerator */
+
+ *ppEnum = new CEnumMediaTypes(this,
+ NULL);
+
+ if (*ppEnum == NULL) {
+ return E_OUTOFMEMORY;
+ }
+
+ return NOERROR;
+}
+
+
+
+/* This is a virtual function that returns a media type corresponding with
+ place iPosition in the list. This base class simply returns an error as
+ we support no media types by default but derived classes should override */
+
+HRESULT CBasePin::GetMediaType(int iPosition, __inout CMediaType *pMediaType)
+{
+ UNREFERENCED_PARAMETER(iPosition);
+ UNREFERENCED_PARAMETER(pMediaType);
+ return E_UNEXPECTED;
+}
+
+
+/* This is a virtual function that returns the current media type version.
+ The base class initialises the media type enumerators with the value 1
+ By default we always returns that same value. A Derived class may change
+ the list of media types available and after doing so it should increment
+ the version either in a method derived from this, or more simply by just
+ incrementing the m_TypeVersion base pin variable. The type enumerators
+ call this when they want to see if their enumerations are out of date */
+
+LONG CBasePin::GetMediaTypeVersion()
+{
+ return m_TypeVersion;
+}
+
+
+/* Increment the cookie representing the current media type version */
+
+void CBasePin::IncrementTypeVersion()
+{
+ InterlockedIncrement(&m_TypeVersion);
+}
+
+
+/* Called by IMediaFilter implementation when the state changes from Stopped
+ to either paused or running and in derived classes could do things like
+ commit memory and grab hardware resource (the default is to do nothing) */
+
+HRESULT
+CBasePin::Active(void)
+{
+ return NOERROR;
+}
+
+/* Called by IMediaFilter implementation when the state changes from
+ to either paused to running and in derived classes could do things like
+ commit memory and grab hardware resource (the default is to do nothing) */
+
+HRESULT
+CBasePin::Run(REFERENCE_TIME tStart)
+{
+ UNREFERENCED_PARAMETER(tStart);
+ return NOERROR;
+}
+
+
+/* Also called by the IMediaFilter implementation when the state changes to
+ Stopped at which point you should decommit allocators and free hardware
+ resources you grabbed in the Active call (default is also to do nothing) */
+
+HRESULT
+CBasePin::Inactive(void)
+{
+ m_bRunTimeError = FALSE;
+ return NOERROR;
+}
+
+
+// Called when no more data will arrive
+STDMETHODIMP
+CBasePin::EndOfStream(void)
+{
+ return S_OK;
+}
+
+
+STDMETHODIMP
+CBasePin::SetSink(IQualityControl * piqc)
+{
+ CAutoLock cObjectLock(m_pLock);
+ if (piqc) ValidateReadPtr(piqc,sizeof(IQualityControl));
+ m_pQSink = piqc;
+ return NOERROR;
+} // SetSink
+
+
+STDMETHODIMP
+CBasePin::Notify(IBaseFilter * pSender, Quality q)
+{
+ UNREFERENCED_PARAMETER(q);
+ UNREFERENCED_PARAMETER(pSender);
+ DbgBreak("IQualityControl::Notify not over-ridden from CBasePin. (IGNORE is OK)");
+ return E_NOTIMPL;
+} //Notify
+
+
+// NewSegment notifies of the start/stop/rate applying to the data
+// about to be received. Default implementation records data and
+// returns S_OK.
+// Override this to pass downstream.
+STDMETHODIMP
+CBasePin::NewSegment(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop,
+ double dRate)
+{
+ m_tStart = tStart;
+ m_tStop = tStop;
+ m_dRate = dRate;
+
+ return S_OK;
+}
+
+
+//=====================================================================
+//=====================================================================
+// Implements CBaseOutputPin
+//=====================================================================
+//=====================================================================
+
+
+CBaseOutputPin::CBaseOutputPin(__in_opt LPCTSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName) :
+ CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),
+ m_pAllocator(NULL),
+ m_pInputPin(NULL)
+{
+ ASSERT(pFilter);
+}
+
+#ifdef UNICODE
+CBaseOutputPin::CBaseOutputPin(__in_opt LPCSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName) :
+ CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),
+ m_pAllocator(NULL),
+ m_pInputPin(NULL)
+{
+ ASSERT(pFilter);
+}
+#endif
+
+/* This is called after a media type has been proposed
+
+ Try to complete the connection by agreeing the allocator
+*/
+HRESULT
+CBaseOutputPin::CompleteConnect(IPin *pReceivePin)
+{
+ UNREFERENCED_PARAMETER(pReceivePin);
+ return DecideAllocator(m_pInputPin, &m_pAllocator);
+}
+
+
+/* This method is called when the output pin is about to try and connect to
+ an input pin. It is at this point that you should try and grab any extra
+ interfaces that you need, in this case IMemInputPin. Because this is
+ only called if we are not currently connected we do NOT need to call
+ BreakConnect. This also makes it easier to derive classes from us as
+ BreakConnect is only called when we actually have to break a connection
+ (or a partly made connection) and not when we are checking a connection */
+
+/* Overriden from CBasePin */
+
+HRESULT
+CBaseOutputPin::CheckConnect(IPin * pPin)
+{
+ HRESULT hr = CBasePin::CheckConnect(pPin);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // get an input pin and an allocator interface
+ hr = pPin->QueryInterface(IID_IMemInputPin, (void **) &m_pInputPin);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return NOERROR;
+}
+
+
+/* Overriden from CBasePin */
+
+HRESULT
+CBaseOutputPin::BreakConnect()
+{
+ /* Release any allocator we hold */
+
+ if (m_pAllocator) {
+ // Always decommit the allocator because a downstream filter may or
+ // may not decommit the connection's allocator. A memory leak could
+ // occur if the allocator is not decommited when a connection is broken.
+ HRESULT hr = m_pAllocator->Decommit();
+ if( FAILED( hr ) ) {
+ return hr;
+ }
+
+ m_pAllocator->Release();
+ m_pAllocator = NULL;
+ }
+
+ /* Release any input pin interface we hold */
+
+ if (m_pInputPin) {
+ m_pInputPin->Release();
+ m_pInputPin = NULL;
+ }
+ return NOERROR;
+}
+
+
+/* This is called when the input pin didn't give us a valid allocator */
+
+HRESULT
+CBaseOutputPin::InitAllocator(__deref_out IMemAllocator **ppAlloc)
+{
+ return CreateMemoryAllocator(ppAlloc);
+}
+
+
+/* Decide on an allocator, override this if you want to use your own allocator
+ Override DecideBufferSize to call SetProperties. If the input pin fails
+ the GetAllocator call then this will construct a CMemAllocator and call
+ DecideBufferSize on that, and if that fails then we are completely hosed.
+ If the you succeed the DecideBufferSize call, we will notify the input
+ pin of the selected allocator. NOTE this is called during Connect() which
+ therefore looks after grabbing and locking the object's critical section */
+
+// We query the input pin for its requested properties and pass this to
+// DecideBufferSize to allow it to fulfill requests that it is happy
+// with (eg most people don't care about alignment and are thus happy to
+// use the downstream pin's alignment request).
+
+HRESULT
+CBaseOutputPin::DecideAllocator(IMemInputPin *pPin, __deref_out IMemAllocator **ppAlloc)
+{
+ HRESULT hr = NOERROR;
+ *ppAlloc = NULL;
+
+ // get downstream prop request
+ // the derived class may modify this in DecideBufferSize, but
+ // we assume that he will consistently modify it the same way,
+ // so we only get it once
+ ALLOCATOR_PROPERTIES prop;
+ ZeroMemory(&prop, sizeof(prop));
+
+ // whatever he returns, we assume prop is either all zeros
+ // or he has filled it out.
+ pPin->GetAllocatorRequirements(&prop);
+
+ // if he doesn't care about alignment, then set it to 1
+ if (prop.cbAlign == 0) {
+ prop.cbAlign = 1;
+ }
+
+ /* Try the allocator provided by the input pin */
+
+ hr = pPin->GetAllocator(ppAlloc);
+ if (SUCCEEDED(hr)) {
+
+ hr = DecideBufferSize(*ppAlloc, &prop);
+ if (SUCCEEDED(hr)) {
+ hr = pPin->NotifyAllocator(*ppAlloc, FALSE);
+ if (SUCCEEDED(hr)) {
+ return NOERROR;
+ }
+ }
+ }
+
+ /* If the GetAllocator failed we may not have an interface */
+
+ if (*ppAlloc) {
+ (*ppAlloc)->Release();
+ *ppAlloc = NULL;
+ }
+
+ /* Try the output pin's allocator by the same method */
+
+ hr = InitAllocator(ppAlloc);
+ if (SUCCEEDED(hr)) {
+
+ // note - the properties passed here are in the same
+ // structure as above and may have been modified by
+ // the previous call to DecideBufferSize
+ hr = DecideBufferSize(*ppAlloc, &prop);
+ if (SUCCEEDED(hr)) {
+ hr = pPin->NotifyAllocator(*ppAlloc, FALSE);
+ if (SUCCEEDED(hr)) {
+ return NOERROR;
+ }
+ }
+ }
+
+ /* Likewise we may not have an interface to release */
+
+ if (*ppAlloc) {
+ (*ppAlloc)->Release();
+ *ppAlloc = NULL;
+ }
+ return hr;
+}
+
+
+/* This returns an empty sample buffer from the allocator WARNING the same
+ dangers and restrictions apply here as described below for Deliver() */
+
+HRESULT
+CBaseOutputPin::GetDeliveryBuffer(__deref_out IMediaSample ** ppSample,
+ __in_opt REFERENCE_TIME * pStartTime,
+ __in_opt REFERENCE_TIME * pEndTime,
+ DWORD dwFlags)
+{
+ if (m_pAllocator != NULL) {
+ return m_pAllocator->GetBuffer(ppSample,pStartTime,pEndTime,dwFlags);
+ } else {
+ return E_NOINTERFACE;
+ }
+}
+
+
+/* Deliver a filled-in sample to the connected input pin. NOTE the object must
+ have locked itself before calling us otherwise we may get halfway through
+ executing this method only to find the filter graph has got in and
+ disconnected us from the input pin. If the filter has no worker threads
+ then the lock is best applied on Receive(), otherwise it should be done
+ when the worker thread is ready to deliver. There is a wee snag to worker
+ threads that this shows up. The worker thread must lock the object when
+ it is ready to deliver a sample, but it may have to wait until a state
+ change has completed, but that may never complete because the state change
+ is waiting for the worker thread to complete. The way to handle this is for
+ the state change code to grab the critical section, then set an abort event
+ for the worker thread, then release the critical section and wait for the
+ worker thread to see the event we set and then signal that it has finished
+ (with another event). At which point the state change code can complete */
+
+// note (if you've still got any breath left after reading that) that you
+// need to release the sample yourself after this call. if the connected
+// input pin needs to hold onto the sample beyond the call, it will addref
+// the sample itself.
+
+// of course you must release this one and call GetDeliveryBuffer for the
+// next. You cannot reuse it directly.
+
+HRESULT
+CBaseOutputPin::Deliver(IMediaSample * pSample)
+{
+ if (m_pInputPin == NULL) {
+ return VFW_E_NOT_CONNECTED;
+ }
+
+#ifdef DXMPERF
+ PERFLOG_DELIVER( m_pName ? m_pName : L"CBaseOutputPin", (IPin *) this, (IPin *) m_pInputPin, pSample, &m_mt );
+#endif // DXMPERF
+
+ return m_pInputPin->Receive(pSample);
+}
+
+
+// called from elsewhere in our filter to pass EOS downstream to
+// our connected input pin
+HRESULT
+CBaseOutputPin::DeliverEndOfStream(void)
+{
+ // remember this is on IPin not IMemInputPin
+ if (m_Connected == NULL) {
+ return VFW_E_NOT_CONNECTED;
+ }
+ return m_Connected->EndOfStream();
+}
+
+
+/* Commit the allocator's memory, this is called through IMediaFilter
+ which is responsible for locking the object before calling us */
+
+HRESULT
+CBaseOutputPin::Active(void)
+{
+ if (m_pAllocator == NULL) {
+ return VFW_E_NO_ALLOCATOR;
+ }
+ return m_pAllocator->Commit();
+}
+
+
+/* Free up or unprepare allocator's memory, this is called through
+ IMediaFilter which is responsible for locking the object first */
+
+HRESULT
+CBaseOutputPin::Inactive(void)
+{
+ m_bRunTimeError = FALSE;
+ if (m_pAllocator == NULL) {
+ return VFW_E_NO_ALLOCATOR;
+ }
+ return m_pAllocator->Decommit();
+}
+
+// we have a default handling of EndOfStream which is to return
+// an error, since this should be called on input pins only
+STDMETHODIMP
+CBaseOutputPin::EndOfStream(void)
+{
+ return E_UNEXPECTED;
+}
+
+
+// BeginFlush should be called on input pins only
+STDMETHODIMP
+CBaseOutputPin::BeginFlush(void)
+{
+ return E_UNEXPECTED;
+}
+
+// EndFlush should be called on input pins only
+STDMETHODIMP
+CBaseOutputPin::EndFlush(void)
+{
+ return E_UNEXPECTED;
+}
+
+// call BeginFlush on the connected input pin
+HRESULT
+CBaseOutputPin::DeliverBeginFlush(void)
+{
+ // remember this is on IPin not IMemInputPin
+ if (m_Connected == NULL) {
+ return VFW_E_NOT_CONNECTED;
+ }
+ return m_Connected->BeginFlush();
+}
+
+// call EndFlush on the connected input pin
+HRESULT
+CBaseOutputPin::DeliverEndFlush(void)
+{
+ // remember this is on IPin not IMemInputPin
+ if (m_Connected == NULL) {
+ return VFW_E_NOT_CONNECTED;
+ }
+ return m_Connected->EndFlush();
+}
+// deliver NewSegment to connected pin
+HRESULT
+CBaseOutputPin::DeliverNewSegment(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop,
+ double dRate)
+{
+ if (m_Connected == NULL) {
+ return VFW_E_NOT_CONNECTED;
+ }
+ return m_Connected->NewSegment(tStart, tStop, dRate);
+}
+
+
+//=====================================================================
+//=====================================================================
+// Implements CBaseInputPin
+//=====================================================================
+//=====================================================================
+
+
+/* Constructor creates a default allocator object */
+
+CBaseInputPin::CBaseInputPin(__in_opt LPCTSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pPinName) :
+ CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),
+ m_pAllocator(NULL),
+ m_bReadOnly(FALSE),
+ m_bFlushing(FALSE)
+{
+ ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));
+}
+
+#ifdef UNICODE
+CBaseInputPin::CBaseInputPin(__in LPCSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pPinName) :
+ CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),
+ m_pAllocator(NULL),
+ m_bReadOnly(FALSE),
+ m_bFlushing(FALSE)
+{
+ ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));
+}
+#endif
+
+/* Destructor releases it's reference count on the default allocator */
+
+CBaseInputPin::~CBaseInputPin()
+{
+ if (m_pAllocator != NULL) {
+ m_pAllocator->Release();
+ m_pAllocator = NULL;
+ }
+}
+
+
+// override this to publicise our interfaces
+STDMETHODIMP
+CBaseInputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ /* Do we know about this interface */
+
+ if (riid == IID_IMemInputPin) {
+ return GetInterface((IMemInputPin *) this, ppv);
+ } else {
+ return CBasePin::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+/* Return the allocator interface that this input pin would like the output
+ pin to use. NOTE subsequent calls to GetAllocator should all return an
+ interface onto the SAME object so we create one object at the start
+
+ Note:
+ The allocator is Release()'d on disconnect and replaced on
+ NotifyAllocator().
+
+ Override this to provide your own allocator.
+*/
+
+STDMETHODIMP
+CBaseInputPin::GetAllocator(
+ __deref_out IMemAllocator **ppAllocator)
+{
+ CheckPointer(ppAllocator,E_POINTER);
+ ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));
+ CAutoLock cObjectLock(m_pLock);
+
+ if (m_pAllocator == NULL) {
+ HRESULT hr = CreateMemoryAllocator(&m_pAllocator);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+ ASSERT(m_pAllocator != NULL);
+ *ppAllocator = m_pAllocator;
+ m_pAllocator->AddRef();
+ return NOERROR;
+}
+
+
+/* Tell the input pin which allocator the output pin is actually going to use
+ Override this if you care - NOTE the locking we do both here and also in
+ GetAllocator is unnecessary but derived classes that do something useful
+ will undoubtedly have to lock the object so this might help remind people */
+
+STDMETHODIMP
+CBaseInputPin::NotifyAllocator(
+ IMemAllocator * pAllocator,
+ BOOL bReadOnly)
+{
+ CheckPointer(pAllocator,E_POINTER);
+ ValidateReadPtr(pAllocator,sizeof(IMemAllocator));
+ CAutoLock cObjectLock(m_pLock);
+
+ IMemAllocator *pOldAllocator = m_pAllocator;
+ pAllocator->AddRef();
+ m_pAllocator = pAllocator;
+
+ if (pOldAllocator != NULL) {
+ pOldAllocator->Release();
+ }
+
+ // the readonly flag indicates whether samples from this allocator should
+ // be regarded as readonly - if true, then inplace transforms will not be
+ // allowed.
+ m_bReadOnly = (BYTE)bReadOnly;
+ return NOERROR;
+}
+
+
+HRESULT
+CBaseInputPin::BreakConnect()
+{
+ /* We don't need our allocator any more */
+ if (m_pAllocator) {
+ // Always decommit the allocator because a downstream filter may or
+ // may not decommit the connection's allocator. A memory leak could
+ // occur if the allocator is not decommited when a pin is disconnected.
+ HRESULT hr = m_pAllocator->Decommit();
+ if( FAILED( hr ) ) {
+ return hr;
+ }
+
+ m_pAllocator->Release();
+ m_pAllocator = NULL;
+ }
+
+ return S_OK;
+}
+
+
+/* Do something with this media sample - this base class checks to see if the
+ format has changed with this media sample and if so checks that the filter
+ will accept it, generating a run time error if not. Once we have raised a
+ run time error we set a flag so that no more samples will be accepted
+
+ It is important that any filter should override this method and implement
+ synchronization so that samples are not processed when the pin is
+ disconnected etc
+*/
+
+STDMETHODIMP
+CBaseInputPin::Receive(IMediaSample *pSample)
+{
+ CheckPointer(pSample,E_POINTER);
+ ValidateReadPtr(pSample,sizeof(IMediaSample));
+ ASSERT(pSample);
+
+ HRESULT hr = CheckStreaming();
+ if (S_OK != hr) {
+ return hr;
+ }
+
+#ifdef DXMPERF
+ PERFLOG_RECEIVE( m_pName ? m_pName : L"CBaseInputPin", (IPin *) m_Connected, (IPin *) this, pSample, &m_mt );
+#endif // DXMPERF
+
+
+ /* Check for IMediaSample2 */
+ IMediaSample2 *pSample2;
+ if (SUCCEEDED(pSample->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) {
+ hr = pSample2->GetProperties(sizeof(m_SampleProps), (PBYTE)&m_SampleProps);
+ pSample2->Release();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ } else {
+ /* Get the properties the hard way */
+ m_SampleProps.cbData = sizeof(m_SampleProps);
+ m_SampleProps.dwTypeSpecificFlags = 0;
+ m_SampleProps.dwStreamId = AM_STREAM_MEDIA;
+ m_SampleProps.dwSampleFlags = 0;
+ if (S_OK == pSample->IsDiscontinuity()) {
+ m_SampleProps.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
+ }
+ if (S_OK == pSample->IsPreroll()) {
+ m_SampleProps.dwSampleFlags |= AM_SAMPLE_PREROLL;
+ }
+ if (S_OK == pSample->IsSyncPoint()) {
+ m_SampleProps.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
+ }
+ if (SUCCEEDED(pSample->GetTime(&m_SampleProps.tStart,
+ &m_SampleProps.tStop))) {
+ m_SampleProps.dwSampleFlags |= AM_SAMPLE_TIMEVALID |
+ AM_SAMPLE_STOPVALID;
+ }
+ if (S_OK == pSample->GetMediaType(&m_SampleProps.pMediaType)) {
+ m_SampleProps.dwSampleFlags |= AM_SAMPLE_TYPECHANGED;
+ }
+ pSample->GetPointer(&m_SampleProps.pbBuffer);
+ m_SampleProps.lActual = pSample->GetActualDataLength();
+ m_SampleProps.cbBuffer = pSample->GetSize();
+ }
+
+ /* Has the format changed in this sample */
+
+ if (!(m_SampleProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED)) {
+ return NOERROR;
+ }
+
+ /* Check the derived class accepts this format */
+ /* This shouldn't fail as the source must call QueryAccept first */
+
+ hr = CheckMediaType((CMediaType *)m_SampleProps.pMediaType);
+
+ if (hr == NOERROR) {
+ return NOERROR;
+ }
+
+ /* Raise a runtime error if we fail the media type */
+
+ m_bRunTimeError = TRUE;
+ EndOfStream();
+ m_pFilter->NotifyEvent(EC_ERRORABORT,VFW_E_TYPE_NOT_ACCEPTED,0);
+ return VFW_E_INVALIDMEDIATYPE;
+}
+
+
+/* Receive multiple samples */
+STDMETHODIMP
+CBaseInputPin::ReceiveMultiple (
+ __in_ecount(nSamples) IMediaSample **pSamples,
+ long nSamples,
+ __out long *nSamplesProcessed)
+{
+ CheckPointer(pSamples,E_POINTER);
+ ValidateReadPtr(pSamples,nSamples * sizeof(IMediaSample *));
+
+ HRESULT hr = S_OK;
+ *nSamplesProcessed = 0;
+ while (nSamples-- > 0) {
+ hr = Receive(pSamples[*nSamplesProcessed]);
+
+ /* S_FALSE means don't send any more */
+ if (hr != S_OK) {
+ break;
+ }
+ (*nSamplesProcessed)++;
+ }
+ return hr;
+}
+
+/* See if Receive() might block */
+STDMETHODIMP
+CBaseInputPin::ReceiveCanBlock()
+{
+ /* Ask all the output pins if they block
+ If there are no output pin assume we do block
+ */
+ int cPins = m_pFilter->GetPinCount();
+ int cOutputPins = 0;
+ for (int c = 0; c < cPins; c++) {
+ CBasePin *pPin = m_pFilter->GetPin(c);
+ if (NULL == pPin) {
+ break;
+ }
+ PIN_DIRECTION pd;
+ HRESULT hr = pPin->QueryDirection(&pd);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ if (pd == PINDIR_OUTPUT) {
+
+ IPin *pConnected;
+ hr = pPin->ConnectedTo(&pConnected);
+ if (SUCCEEDED(hr)) {
+ ASSERT(pConnected != NULL);
+ cOutputPins++;
+ IMemInputPin *pInputPin;
+ hr = pConnected->QueryInterface(
+ IID_IMemInputPin,
+ (void **)&pInputPin);
+ pConnected->Release();
+ if (SUCCEEDED(hr)) {
+ hr = pInputPin->ReceiveCanBlock();
+ pInputPin->Release();
+ if (hr != S_FALSE) {
+ return S_OK;
+ }
+ } else {
+ /* There's a transport we don't understand here */
+ return S_OK;
+ }
+ }
+ }
+ }
+ return cOutputPins == 0 ? S_OK : S_FALSE;
+}
+
+// Default handling for BeginFlush - call at the beginning
+// of your implementation (makes sure that all Receive calls
+// fail). After calling this, you need to free any queued data
+// and then call downstream.
+STDMETHODIMP
+CBaseInputPin::BeginFlush(void)
+{
+ // BeginFlush is NOT synchronized with streaming but is part of
+ // a control action - hence we synchronize with the filter
+ CAutoLock lck(m_pLock);
+
+ // if we are already in mid-flush, this is probably a mistake
+ // though not harmful - try to pick it up for now so I can think about it
+ ASSERT(!m_bFlushing);
+
+ // first thing to do is ensure that no further Receive calls succeed
+ m_bFlushing = TRUE;
+
+ // now discard any data and call downstream - must do that
+ // in derived classes
+ return S_OK;
+}
+
+// default handling for EndFlush - call at end of your implementation
+// - before calling this, ensure that there is no queued data and no thread
+// pushing any more without a further receive, then call downstream,
+// then call this method to clear the m_bFlushing flag and re-enable
+// receives
+STDMETHODIMP
+CBaseInputPin::EndFlush(void)
+{
+ // Endlush is NOT synchronized with streaming but is part of
+ // a control action - hence we synchronize with the filter
+ CAutoLock lck(m_pLock);
+
+ // almost certainly a mistake if we are not in mid-flush
+ ASSERT(m_bFlushing);
+
+ // before calling, sync with pushing thread and ensure
+ // no more data is going downstream, then call EndFlush on
+ // downstream pins.
+
+ // now re-enable Receives
+ m_bFlushing = FALSE;
+
+ // No more errors
+ m_bRunTimeError = FALSE;
+
+ return S_OK;
+}
+
+
+STDMETHODIMP
+CBaseInputPin::Notify(IBaseFilter * pSender, Quality q)
+{
+ UNREFERENCED_PARAMETER(q);
+ CheckPointer(pSender,E_POINTER);
+ ValidateReadPtr(pSender,sizeof(IBaseFilter));
+ DbgBreak("IQuality::Notify called on an input pin");
+ return NOERROR;
+} // Notify
+
+/* Free up or unprepare allocator's memory, this is called through
+ IMediaFilter which is responsible for locking the object first */
+
+HRESULT
+CBaseInputPin::Inactive(void)
+{
+ m_bRunTimeError = FALSE;
+ if (m_pAllocator == NULL) {
+ return VFW_E_NO_ALLOCATOR;
+ }
+
+ m_bFlushing = FALSE;
+
+ return m_pAllocator->Decommit();
+}
+
+// what requirements do we have of the allocator - override if you want
+// to support other people's allocators but need a specific alignment
+// or prefix.
+STDMETHODIMP
+CBaseInputPin::GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps)
+{
+ UNREFERENCED_PARAMETER(pProps);
+ return E_NOTIMPL;
+}
+
+// Check if it's OK to process data
+//
+HRESULT
+CBaseInputPin::CheckStreaming()
+{
+ // Shouldn't be able to get any data if we're not connected!
+ ASSERT(IsConnected());
+
+ // Don't process stuff in Stopped state
+ if (IsStopped()) {
+ return VFW_E_WRONG_STATE;
+ }
+ if (m_bFlushing) {
+ return S_FALSE;
+ }
+ if (m_bRunTimeError) {
+ return VFW_E_RUNTIME_ERROR;
+ }
+ return S_OK;
+}
+
+// Pass on the Quality notification q to
+// a. Our QualityControl sink (if we have one) or else
+// b. to our upstream filter
+// and if that doesn't work, throw it away with a bad return code
+HRESULT
+CBaseInputPin::PassNotify(Quality& q)
+{
+ // We pass the message on, which means that we find the quality sink
+ // for our input pin and send it there
+
+ DbgLog((LOG_TRACE,3,TEXT("Passing Quality notification through transform")));
+ if (m_pQSink!=NULL) {
+ return m_pQSink->Notify(m_pFilter, q);
+ } else {
+ // no sink set, so pass it upstream
+ HRESULT hr;
+ IQualityControl * pIQC;
+
+ hr = VFW_E_NOT_FOUND; // default
+ if (m_Connected) {
+ m_Connected->QueryInterface(IID_IQualityControl, (void**)&pIQC);
+
+ if (pIQC!=NULL) {
+ hr = pIQC->Notify(m_pFilter, q);
+ pIQC->Release();
+ }
+ }
+ return hr;
+ }
+
+} // PassNotify
+
+//=====================================================================
+//=====================================================================
+// Memory allocation class, implements CMediaSample
+//=====================================================================
+//=====================================================================
+
+
+/* NOTE The implementation of this class calls the CUnknown constructor with
+ a NULL outer unknown pointer. This has the effect of making us a self
+ contained class, ie any QueryInterface, AddRef or Release calls will be
+ routed to the class's NonDelegatingUnknown methods. You will typically
+ find that the classes that do this then override one or more of these
+ virtual functions to provide more specialised behaviour. A good example
+ of this is where a class wants to keep the QueryInterface internal but
+ still wants it's lifetime controlled by the external object */
+
+/* The last two parameters have default values of NULL and zero */
+
+CMediaSample::CMediaSample(__in_opt LPCTSTR pName,
+ __in_opt CBaseAllocator *pAllocator,
+ __inout_opt HRESULT *phr,
+ __in_bcount_opt(length) LPBYTE pBuffer,
+ LONG length) :
+ m_pBuffer(pBuffer), // Initialise the buffer
+ m_cbBuffer(length), // And it's length
+ m_lActual(length), // By default, actual = length
+ m_pMediaType(NULL), // No media type change
+ m_dwFlags(0), // Nothing set
+ m_cRef(0), // 0 ref count
+ m_dwTypeSpecificFlags(0), // Type specific flags
+ m_dwStreamId(AM_STREAM_MEDIA), // Stream id
+ m_pAllocator(pAllocator) // Allocator
+{
+#ifdef DXMPERF
+ PERFLOG_CTOR( pName ? pName : L"CMediaSample", (IMediaSample *) this );
+#endif // DXMPERF
+
+ /* We must have an owner and it must also be derived from class
+ CBaseAllocator BUT we do not hold a reference count on it */
+
+ ASSERT(pAllocator);
+
+ if (length < 0) {
+ *phr = VFW_E_BUFFER_OVERFLOW;
+ m_cbBuffer = 0;
+ }
+}
+
+#ifdef UNICODE
+CMediaSample::CMediaSample(__in_opt LPCSTR pName,
+ __in_opt CBaseAllocator *pAllocator,
+ __inout_opt HRESULT *phr,
+ __in_bcount_opt(length) LPBYTE pBuffer,
+ LONG length) :
+ m_pBuffer(pBuffer), // Initialise the buffer
+ m_cbBuffer(length), // And it's length
+ m_lActual(length), // By default, actual = length
+ m_pMediaType(NULL), // No media type change
+ m_dwFlags(0), // Nothing set
+ m_cRef(0), // 0 ref count
+ m_dwTypeSpecificFlags(0), // Type specific flags
+ m_dwStreamId(AM_STREAM_MEDIA), // Stream id
+ m_pAllocator(pAllocator) // Allocator
+{
+#ifdef DXMPERF
+ PERFLOG_CTOR( L"CMediaSample", (IMediaSample *) this );
+#endif // DXMPERF
+
+ /* We must have an owner and it must also be derived from class
+ CBaseAllocator BUT we do not hold a reference count on it */
+
+ ASSERT(pAllocator);
+}
+#endif
+
+/* Destructor deletes the media type memory */
+
+CMediaSample::~CMediaSample()
+{
+#ifdef DXMPERF
+ PERFLOG_DTOR( L"CMediaSample", (IMediaSample *) this );
+#endif // DXMPERF
+
+ if (m_pMediaType) {
+ DeleteMediaType(m_pMediaType);
+ }
+}
+
+/* Override this to publicise our interfaces */
+
+STDMETHODIMP
+CMediaSample::QueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ if (riid == IID_IMediaSample ||
+ riid == IID_IMediaSample2 ||
+ riid == IID_IUnknown) {
+ return GetInterface((IMediaSample *) this, ppv);
+ } else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+STDMETHODIMP_(ULONG)
+CMediaSample::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+
+// -- CMediaSample lifetimes --
+//
+// On final release of this sample buffer it is not deleted but
+// returned to the freelist of the owning memory allocator
+//
+// The allocator may be waiting for the last buffer to be placed on the free
+// list in order to decommit all the memory, so the ReleaseBuffer() call may
+// result in this sample being deleted. We also need to hold a refcount on
+// the allocator to stop that going away until we have finished with this.
+// However, we cannot release the allocator before the ReleaseBuffer, as the
+// release may cause us to be deleted. Similarly we can't do it afterwards.
+//
+// Thus we must leave it to the allocator to hold an addref on our behalf.
+// When he issues us in GetBuffer, he addref's himself. When ReleaseBuffer
+// is called, he releases himself, possibly causing us and him to be deleted.
+
+
+STDMETHODIMP_(ULONG)
+CMediaSample::Release()
+{
+ /* Decrement our own private reference count */
+ LONG lRef;
+ if (m_cRef == 1) {
+ lRef = 0;
+ m_cRef = 0;
+ } else {
+ lRef = InterlockedDecrement(&m_cRef);
+ }
+ ASSERT(lRef >= 0);
+
+ DbgLog((LOG_MEMORY,3,TEXT(" Unknown %X ref-- = %d"),
+ this, m_cRef));
+
+ /* Did we release our final reference count */
+ if (lRef == 0) {
+ /* Free all resources */
+ if (m_dwFlags & Sample_TypeChanged) {
+ SetMediaType(NULL);
+ }
+ ASSERT(m_pMediaType == NULL);
+ m_dwFlags = 0;
+ m_dwTypeSpecificFlags = 0;
+ m_dwStreamId = AM_STREAM_MEDIA;
+
+ /* This may cause us to be deleted */
+ // Our refcount is reliably 0 thus no-one will mess with us
+ m_pAllocator->ReleaseBuffer(this);
+ }
+ return (ULONG)lRef;
+}
+
+
+// set the buffer pointer and length. Used by allocators that
+// want variable sized pointers or pointers into already-read data.
+// This is only available through a CMediaSample* not an IMediaSample*
+// and so cannot be changed by clients.
+HRESULT
+CMediaSample::SetPointer(__in_bcount(cBytes) BYTE * ptr, LONG cBytes)
+{
+ if (cBytes < 0) {
+ return VFW_E_BUFFER_OVERFLOW;
+ }
+ m_pBuffer = ptr; // new buffer area (could be null)
+ m_cbBuffer = cBytes; // length of buffer
+ m_lActual = cBytes; // length of data in buffer (assume full)
+
+ return S_OK;
+}
+
+
+// get me a read/write pointer to this buffer's memory. I will actually
+// want to use sizeUsed bytes.
+STDMETHODIMP
+CMediaSample::GetPointer(__deref_out BYTE ** ppBuffer)
+{
+ ValidateReadWritePtr(ppBuffer,sizeof(BYTE *));
+
+ // creator must have set pointer either during
+ // constructor or by SetPointer
+ ASSERT(m_pBuffer);
+
+ *ppBuffer = m_pBuffer;
+ return NOERROR;
+}
+
+
+// return the size in bytes of this buffer
+STDMETHODIMP_(LONG)
+CMediaSample::GetSize(void)
+{
+ return m_cbBuffer;
+}
+
+
+// get the stream time at which this sample should start and finish.
+STDMETHODIMP
+CMediaSample::GetTime(
+ __out REFERENCE_TIME * pTimeStart, // put time here
+ __out REFERENCE_TIME * pTimeEnd
+)
+{
+ ValidateReadWritePtr(pTimeStart,sizeof(REFERENCE_TIME));
+ ValidateReadWritePtr(pTimeEnd,sizeof(REFERENCE_TIME));
+
+ if (!(m_dwFlags & Sample_StopValid)) {
+ if (!(m_dwFlags & Sample_TimeValid)) {
+ return VFW_E_SAMPLE_TIME_NOT_SET;
+ } else {
+ *pTimeStart = m_Start;
+
+ // Make sure old stuff works
+ *pTimeEnd = m_Start + 1;
+ return VFW_S_NO_STOP_TIME;
+ }
+ }
+
+ *pTimeStart = m_Start;
+ *pTimeEnd = m_End;
+ return NOERROR;
+}
+
+
+// Set the stream time at which this sample should start and finish.
+// NULL pointers means the time is reset
+STDMETHODIMP
+CMediaSample::SetTime(
+ __in_opt REFERENCE_TIME * pTimeStart,
+ __in_opt REFERENCE_TIME * pTimeEnd
+)
+{
+ if (pTimeStart == NULL) {
+ ASSERT(pTimeEnd == NULL);
+ m_dwFlags &= ~(Sample_TimeValid | Sample_StopValid);
+ } else {
+ if (pTimeEnd == NULL) {
+ m_Start = *pTimeStart;
+ m_dwFlags |= Sample_TimeValid;
+ m_dwFlags &= ~Sample_StopValid;
+ } else {
+ ValidateReadPtr(pTimeStart,sizeof(REFERENCE_TIME));
+ ValidateReadPtr(pTimeEnd,sizeof(REFERENCE_TIME));
+ ASSERT(*pTimeEnd >= *pTimeStart);
+
+ m_Start = *pTimeStart;
+ m_End = *pTimeEnd;
+ m_dwFlags |= Sample_TimeValid | Sample_StopValid;
+ }
+ }
+ return NOERROR;
+}
+
+
+// get the media times (eg bytes) for this sample
+STDMETHODIMP
+CMediaSample::GetMediaTime(
+ __out LONGLONG * pTimeStart,
+ __out LONGLONG * pTimeEnd
+)
+{
+ ValidateReadWritePtr(pTimeStart,sizeof(LONGLONG));
+ ValidateReadWritePtr(pTimeEnd,sizeof(LONGLONG));
+
+ if (!(m_dwFlags & Sample_MediaTimeValid)) {
+ return VFW_E_MEDIA_TIME_NOT_SET;
+ }
+
+ *pTimeStart = m_MediaStart;
+ *pTimeEnd = (m_MediaStart + m_MediaEnd);
+ return NOERROR;
+}
+
+
+// Set the media times for this sample
+STDMETHODIMP
+CMediaSample::SetMediaTime(
+ __in_opt LONGLONG * pTimeStart,
+ __in_opt LONGLONG * pTimeEnd
+)
+{
+ if (pTimeStart == NULL) {
+ ASSERT(pTimeEnd == NULL);
+ m_dwFlags &= ~Sample_MediaTimeValid;
+ } else {
+ if (NULL == pTimeEnd) {
+ return E_POINTER;
+ }
+ ValidateReadPtr(pTimeStart,sizeof(LONGLONG));
+ ValidateReadPtr(pTimeEnd,sizeof(LONGLONG));
+ ASSERT(*pTimeEnd >= *pTimeStart);
+
+ m_MediaStart = *pTimeStart;
+ m_MediaEnd = (LONG)(*pTimeEnd - *pTimeStart);
+ m_dwFlags |= Sample_MediaTimeValid;
+ }
+ return NOERROR;
+}
+
+
+STDMETHODIMP
+CMediaSample::IsSyncPoint(void)
+{
+ if (m_dwFlags & Sample_SyncPoint) {
+ return S_OK;
+ } else {
+ return S_FALSE;
+ }
+}
+
+
+STDMETHODIMP
+CMediaSample::SetSyncPoint(BOOL bIsSyncPoint)
+{
+ if (bIsSyncPoint) {
+ m_dwFlags |= Sample_SyncPoint;
+ } else {
+ m_dwFlags &= ~Sample_SyncPoint;
+ }
+ return NOERROR;
+}
+
+// returns S_OK if there is a discontinuity in the data (this same is
+// not a continuation of the previous stream of data
+// - there has been a seek).
+STDMETHODIMP
+CMediaSample::IsDiscontinuity(void)
+{
+ if (m_dwFlags & Sample_Discontinuity) {
+ return S_OK;
+ } else {
+ return S_FALSE;
+ }
+}
+
+// set the discontinuity property - TRUE if this sample is not a
+// continuation, but a new sample after a seek.
+STDMETHODIMP
+CMediaSample::SetDiscontinuity(BOOL bDiscont)
+{
+ // should be TRUE or FALSE
+ if (bDiscont) {
+ m_dwFlags |= Sample_Discontinuity;
+ } else {
+ m_dwFlags &= ~Sample_Discontinuity;
+ }
+ return S_OK;
+}
+
+STDMETHODIMP
+CMediaSample::IsPreroll(void)
+{
+ if (m_dwFlags & Sample_Preroll) {
+ return S_OK;
+ } else {
+ return S_FALSE;
+ }
+}
+
+
+STDMETHODIMP
+CMediaSample::SetPreroll(BOOL bIsPreroll)
+{
+ if (bIsPreroll) {
+ m_dwFlags |= Sample_Preroll;
+ } else {
+ m_dwFlags &= ~Sample_Preroll;
+ }
+ return NOERROR;
+}
+
+STDMETHODIMP_(LONG)
+CMediaSample::GetActualDataLength(void)
+{
+ return m_lActual;
+}
+
+
+STDMETHODIMP
+CMediaSample::SetActualDataLength(LONG lActual)
+{
+ if (lActual > m_cbBuffer || lActual < 0) {
+ ASSERT(lActual <= GetSize());
+ return VFW_E_BUFFER_OVERFLOW;
+ }
+ m_lActual = lActual;
+ return NOERROR;
+}
+
+
+/* These allow for limited format changes in band */
+
+STDMETHODIMP
+CMediaSample::GetMediaType(__deref_out AM_MEDIA_TYPE **ppMediaType)
+{
+ ValidateReadWritePtr(ppMediaType,sizeof(AM_MEDIA_TYPE *));
+ ASSERT(ppMediaType);
+
+ /* Do we have a new media type for them */
+
+ if (!(m_dwFlags & Sample_TypeChanged)) {
+ ASSERT(m_pMediaType == NULL);
+ *ppMediaType = NULL;
+ return S_FALSE;
+ }
+
+ ASSERT(m_pMediaType);
+
+ /* Create a copy of our media type */
+
+ *ppMediaType = CreateMediaType(m_pMediaType);
+ if (*ppMediaType == NULL) {
+ return E_OUTOFMEMORY;
+ }
+ return NOERROR;
+}
+
+
+/* Mark this sample as having a different format type */
+
+STDMETHODIMP
+CMediaSample::SetMediaType(__in_opt AM_MEDIA_TYPE *pMediaType)
+{
+ /* Delete the current media type */
+
+ if (m_pMediaType) {
+ DeleteMediaType(m_pMediaType);
+ m_pMediaType = NULL;
+ }
+
+ /* Mechanism for resetting the format type */
+
+ if (pMediaType == NULL) {
+ m_dwFlags &= ~Sample_TypeChanged;
+ return NOERROR;
+ }
+
+ ASSERT(pMediaType);
+ ValidateReadPtr(pMediaType,sizeof(AM_MEDIA_TYPE));
+
+ /* Take a copy of the media type */
+
+ m_pMediaType = CreateMediaType(pMediaType);
+ if (m_pMediaType == NULL) {
+ m_dwFlags &= ~Sample_TypeChanged;
+ return E_OUTOFMEMORY;
+ }
+
+ m_dwFlags |= Sample_TypeChanged;
+ return NOERROR;
+}
+
+// Set and get properties (IMediaSample2)
+STDMETHODIMP CMediaSample::GetProperties(
+ DWORD cbProperties,
+ __out_bcount(cbProperties) BYTE * pbProperties
+)
+{
+ if (0 != cbProperties) {
+ CheckPointer(pbProperties, E_POINTER);
+ // Return generic stuff up to the length
+ AM_SAMPLE2_PROPERTIES Props;
+ Props.cbData = min(cbProperties, sizeof(Props));
+ Props.dwSampleFlags = m_dwFlags & ~Sample_MediaTimeValid;
+ Props.dwTypeSpecificFlags = m_dwTypeSpecificFlags;
+ Props.pbBuffer = m_pBuffer;
+ Props.cbBuffer = m_cbBuffer;
+ Props.lActual = m_lActual;
+ Props.tStart = m_Start;
+ Props.tStop = m_End;
+ Props.dwStreamId = m_dwStreamId;
+ if (m_dwFlags & AM_SAMPLE_TYPECHANGED) {
+ Props.pMediaType = m_pMediaType;
+ } else {
+ Props.pMediaType = NULL;
+ }
+ CopyMemory(pbProperties, &Props, Props.cbData);
+ }
+ return S_OK;
+}
+
+#define CONTAINS_FIELD(type, field, offset) \
+ ((FIELD_OFFSET(type, field) + sizeof(((type *)0)->field)) <= offset)
+
+HRESULT CMediaSample::SetProperties(
+ DWORD cbProperties,
+ __in_bcount(cbProperties) const BYTE * pbProperties
+)
+{
+
+ /* Generic properties */
+ AM_MEDIA_TYPE *pMediaType = NULL;
+
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbData, cbProperties)) {
+ CheckPointer(pbProperties, E_POINTER);
+ AM_SAMPLE2_PROPERTIES *pProps =
+ (AM_SAMPLE2_PROPERTIES *)pbProperties;
+
+ /* Don't use more data than is actually there */
+ if (pProps->cbData < cbProperties) {
+ cbProperties = pProps->cbData;
+ }
+ /* We only handle IMediaSample2 */
+ if (cbProperties > sizeof(*pProps) ||
+ pProps->cbData > sizeof(*pProps)) {
+ return E_INVALIDARG;
+ }
+ /* Do checks first, the assignments (for backout) */
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {
+ /* Check the flags */
+ if (pProps->dwSampleFlags &
+ (~Sample_ValidFlags | Sample_MediaTimeValid)) {
+ return E_INVALIDARG;
+ }
+ /* Check a flag isn't being set for a property
+ not being provided
+ */
+ if ((pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) &&
+ !(m_dwFlags & AM_SAMPLE_TIMEVALID) &&
+ !CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {
+ return E_INVALIDARG;
+ }
+ }
+ /* NB - can't SET the pointer or size */
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pbBuffer, cbProperties)) {
+
+ /* Check pbBuffer */
+ if (pProps->pbBuffer != 0 && pProps->pbBuffer != m_pBuffer) {
+ return E_INVALIDARG;
+ }
+ }
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties)) {
+
+ /* Check cbBuffer */
+ if (pProps->cbBuffer != 0 && pProps->cbBuffer != m_cbBuffer) {
+ return E_INVALIDARG;
+ }
+ }
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties) &&
+ CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {
+
+ /* Check lActual */
+ if (pProps->cbBuffer < pProps->lActual) {
+ return E_INVALIDARG;
+ }
+ }
+
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {
+
+ /* Check pMediaType */
+ if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {
+ CheckPointer(pProps->pMediaType, E_POINTER);
+ pMediaType = CreateMediaType(pProps->pMediaType);
+ if (pMediaType == NULL) {
+ return E_OUTOFMEMORY;
+ }
+ }
+ }
+
+ /* Now do the assignments */
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwStreamId, cbProperties)) {
+ m_dwStreamId = pProps->dwStreamId;
+ }
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {
+ /* Set the flags */
+ m_dwFlags = pProps->dwSampleFlags |
+ (m_dwFlags & Sample_MediaTimeValid);
+ m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
+ } else {
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwTypeSpecificFlags, cbProperties)) {
+ m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
+ }
+ }
+
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {
+ /* Set lActual */
+ m_lActual = pProps->lActual;
+ }
+
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {
+
+ /* Set the times */
+ m_End = pProps->tStop;
+ }
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStart, cbProperties)) {
+
+ /* Set the times */
+ m_Start = pProps->tStart;
+ }
+
+ if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {
+ /* Set pMediaType */
+ if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {
+ if (m_pMediaType != NULL) {
+ DeleteMediaType(m_pMediaType);
+ }
+ m_pMediaType = pMediaType;
+ }
+ }
+
+ /* Fix up the type changed flag to correctly reflect the current state
+ If, for instance the input contained no type change but the
+ output does then if we don't do this we'd lose the
+ output media type.
+ */
+ if (m_pMediaType) {
+ m_dwFlags |= Sample_TypeChanged;
+ } else {
+ m_dwFlags &= ~Sample_TypeChanged;
+ }
+ }
+
+ return S_OK;
+}
+
+
+//
+// The streaming thread calls IPin::NewSegment(), IPin::EndOfStream(),
+// IMemInputPin::Receive() and IMemInputPin::ReceiveMultiple() on the
+// connected input pin. The application thread calls Block(). The
+// following class members can only be called by the streaming thread.
+//
+// Deliver()
+// DeliverNewSegment()
+// StartUsingOutputPin()
+// StopUsingOutputPin()
+// ChangeOutputFormat()
+// ChangeMediaType()
+// DynamicReconnect()
+//
+// The following class members can only be called by the application thread.
+//
+// Block()
+// SynchronousBlockOutputPin()
+// AsynchronousBlockOutputPin()
+//
+
+CDynamicOutputPin::CDynamicOutputPin(
+ __in_opt LPCTSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName) :
+ CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),
+ m_hStopEvent(NULL),
+ m_pGraphConfig(NULL),
+ m_bPinUsesReadOnlyAllocator(FALSE),
+ m_BlockState(NOT_BLOCKED),
+ m_hUnblockOutputPinEvent(NULL),
+ m_hNotifyCallerPinBlockedEvent(NULL),
+ m_dwBlockCallerThreadID(0),
+ m_dwNumOutstandingOutputPinUsers(0)
+{
+ HRESULT hr = Initialize();
+ if( FAILED( hr ) ) {
+ *phr = hr;
+ return;
+ }
+}
+
+#ifdef UNICODE
+CDynamicOutputPin::CDynamicOutputPin(
+ __in_opt LPCSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName) :
+ CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),
+ m_hStopEvent(NULL),
+ m_pGraphConfig(NULL),
+ m_bPinUsesReadOnlyAllocator(FALSE),
+ m_BlockState(NOT_BLOCKED),
+ m_hUnblockOutputPinEvent(NULL),
+ m_hNotifyCallerPinBlockedEvent(NULL),
+ m_dwBlockCallerThreadID(0),
+ m_dwNumOutstandingOutputPinUsers(0)
+{
+ HRESULT hr = Initialize();
+ if( FAILED( hr ) ) {
+ *phr = hr;
+ return;
+ }
+}
+#endif
+
+CDynamicOutputPin::~CDynamicOutputPin()
+{
+ if(NULL != m_hUnblockOutputPinEvent) {
+ // This call should not fail because we have access to m_hUnblockOutputPinEvent
+ // and m_hUnblockOutputPinEvent is a valid event.
+ EXECUTE_ASSERT(::CloseHandle(m_hUnblockOutputPinEvent));
+ }
+
+ if(NULL != m_hNotifyCallerPinBlockedEvent) {
+ // This call should not fail because we have access to m_hNotifyCallerPinBlockedEvent
+ // and m_hNotifyCallerPinBlockedEvent is a valid event.
+ EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
+ }
+}
+
+HRESULT CDynamicOutputPin::Initialize(void)
+{
+ m_hUnblockOutputPinEvent = ::CreateEvent( NULL, // The event will have the default security descriptor.
+ TRUE, // This is a manual reset event.
+ TRUE, // The event is initially signaled.
+ NULL ); // The event is not named.
+
+ // CreateEvent() returns NULL if an error occurs.
+ if(NULL == m_hUnblockOutputPinEvent) {
+ return AmGetLastErrorToHResult();
+ }
+
+ // Set flag to say we can reconnect while streaming.
+ SetReconnectWhenActive(true);
+
+ return S_OK;
+}
+
+STDMETHODIMP CDynamicOutputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ if(riid == IID_IPinFlowControl) {
+ return GetInterface(static_cast(this), ppv);
+ } else {
+ return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+STDMETHODIMP CDynamicOutputPin::Disconnect(void)
+{
+ CAutoLock cObjectLock(m_pLock);
+ return DisconnectInternal();
+}
+
+STDMETHODIMP CDynamicOutputPin::Block(DWORD dwBlockFlags, HANDLE hEvent)
+{
+ const DWORD VALID_FLAGS = AM_PIN_FLOW_CONTROL_BLOCK;
+
+ // Check for illegal flags.
+ if(dwBlockFlags & ~VALID_FLAGS) {
+ return E_INVALIDARG;
+ }
+
+ // Make sure the event is unsignaled.
+ if((dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) && (NULL != hEvent)) {
+ if( !::ResetEvent( hEvent ) ) {
+ return AmGetLastErrorToHResult();
+ }
+ }
+
+ // No flags are set if we are unblocking the output pin.
+ if(0 == dwBlockFlags) {
+
+ // This parameter should be NULL because unblock operations are always synchronous.
+ // There is no need to notify the caller when the event is done.
+ if(NULL != hEvent) {
+ return E_INVALIDARG;
+ }
+ }
+
+ #ifdef DEBUG
+ AssertValid();
+ #endif // DEBUG
+
+ HRESULT hr;
+
+ if(dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) {
+ // IPinFlowControl::Block()'s hEvent parameter is NULL if the block is synchronous.
+ // If hEvent is not NULL, the block is asynchronous.
+ if(NULL == hEvent) {
+ hr = SynchronousBlockOutputPin();
+ } else {
+ hr = AsynchronousBlockOutputPin(hEvent);
+ }
+ } else {
+ hr = UnblockOutputPin();
+ }
+
+ #ifdef DEBUG
+ AssertValid();
+ #endif // DEBUG
+
+ if(FAILED(hr)) {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+HRESULT CDynamicOutputPin::SynchronousBlockOutputPin(void)
+{
+ HANDLE hNotifyCallerPinBlockedEvent = :: CreateEvent( NULL, // The event will have the default security attributes.
+ FALSE, // This is an automatic reset event.
+ FALSE, // The event is initially unsignaled.
+ NULL ); // The event is not named.
+
+ // CreateEvent() returns NULL if an error occurs.
+ if(NULL == hNotifyCallerPinBlockedEvent) {
+ return AmGetLastErrorToHResult();
+ }
+
+ HRESULT hr = AsynchronousBlockOutputPin(hNotifyCallerPinBlockedEvent);
+ if(FAILED(hr)) {
+ // This call should not fail because we have access to hNotifyCallerPinBlockedEvent
+ // and hNotifyCallerPinBlockedEvent is a valid event.
+ EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));
+
+ return hr;
+ }
+
+ hr = WaitEvent(hNotifyCallerPinBlockedEvent);
+
+ // This call should not fail because we have access to hNotifyCallerPinBlockedEvent
+ // and hNotifyCallerPinBlockedEvent is a valid event.
+ EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));
+
+ if(FAILED(hr)) {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+HRESULT CDynamicOutputPin::AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent)
+{
+ // This function holds the m_BlockStateLock because it uses
+ // m_dwBlockCallerThreadID, m_BlockState and
+ // m_hNotifyCallerPinBlockedEvent.
+ CAutoLock alBlockStateLock(&m_BlockStateLock);
+
+ if(NOT_BLOCKED != m_BlockState) {
+ if(m_dwBlockCallerThreadID == ::GetCurrentThreadId()) {
+ return VFW_E_PIN_ALREADY_BLOCKED_ON_THIS_THREAD;
+ } else {
+ return VFW_E_PIN_ALREADY_BLOCKED;
+ }
+ }
+
+ BOOL fSuccess = ::DuplicateHandle( ::GetCurrentProcess(),
+ hNotifyCallerPinBlockedEvent,
+ ::GetCurrentProcess(),
+ &m_hNotifyCallerPinBlockedEvent,
+ EVENT_MODIFY_STATE,
+ FALSE,
+ 0 );
+ if( !fSuccess ) {
+ return AmGetLastErrorToHResult();
+ }
+
+ m_BlockState = PENDING;
+ m_dwBlockCallerThreadID = ::GetCurrentThreadId();
+
+ // The output pin cannot be blocked if the streaming thread is
+ // calling IPin::NewSegment(), IPin::EndOfStream(), IMemInputPin::Receive()
+ // or IMemInputPin::ReceiveMultiple() on the connected input pin. Also, it
+ // cannot be blocked if the streaming thread is calling DynamicReconnect(),
+ // ChangeMediaType() or ChangeOutputFormat().
+ if(!StreamingThreadUsingOutputPin()) {
+
+ // The output pin can be immediately blocked.
+ BlockOutputPin();
+ }
+
+ return S_OK;
+}
+
+void CDynamicOutputPin::BlockOutputPin(void)
+{
+ // The caller should always hold the m_BlockStateLock because this function
+ // uses m_BlockState and m_hNotifyCallerPinBlockedEvent.
+ ASSERT(CritCheckIn(&m_BlockStateLock));
+
+ // This function should not be called if the streaming thread is modifying
+ // the connection state or it's passing data downstream.
+ ASSERT(!StreamingThreadUsingOutputPin());
+
+ // This should not fail because we successfully created the event
+ // and we have the security permissions to change it's state.
+ EXECUTE_ASSERT(::ResetEvent(m_hUnblockOutputPinEvent));
+
+ // This event should not fail because AsynchronousBlockOutputPin() successfully
+ // duplicated this handle and we have the appropriate security permissions.
+ EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));
+ EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
+
+ m_BlockState = BLOCKED;
+ m_hNotifyCallerPinBlockedEvent = NULL;
+}
+
+HRESULT CDynamicOutputPin::UnblockOutputPin(void)
+{
+ // UnblockOutputPin() holds the m_BlockStateLock because it
+ // uses m_BlockState, m_dwBlockCallerThreadID and
+ // m_hNotifyCallerPinBlockedEvent.
+ CAutoLock alBlockStateLock(&m_BlockStateLock);
+
+ if(NOT_BLOCKED == m_BlockState) {
+ return S_FALSE;
+ }
+
+ // This should not fail because we successfully created the event
+ // and we have the security permissions to change it's state.
+ EXECUTE_ASSERT(::SetEvent(m_hUnblockOutputPinEvent));
+
+ // Cancel the block operation if it's still pending.
+ if(NULL != m_hNotifyCallerPinBlockedEvent) {
+ // This event should not fail because AsynchronousBlockOutputPin() successfully
+ // duplicated this handle and we have the appropriate security permissions.
+ EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));
+ EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
+ }
+
+ m_BlockState = NOT_BLOCKED;
+ m_dwBlockCallerThreadID = 0;
+ m_hNotifyCallerPinBlockedEvent = NULL;
+
+ return S_OK;
+}
+
+HRESULT CDynamicOutputPin::StartUsingOutputPin(void)
+{
+ // The caller should not hold m_BlockStateLock. If the caller does,
+ // a deadlock could occur.
+ ASSERT(CritCheckOut(&m_BlockStateLock));
+
+ CAutoLock alBlockStateLock(&m_BlockStateLock);
+
+ #ifdef DEBUG
+ AssertValid();
+ #endif // DEBUG
+
+ // Are we in the middle of a block operation?
+ while(BLOCKED == m_BlockState) {
+ m_BlockStateLock.Unlock();
+
+ // If this ASSERT fires, a deadlock could occur. The caller should make sure
+ // that this thread never acquires the Block State lock more than once.
+ ASSERT(CritCheckOut( &m_BlockStateLock ));
+
+ // WaitForMultipleObjects() returns WAIT_OBJECT_0 if the unblock event
+ // is fired. It returns WAIT_OBJECT_0 + 1 if the stop event if fired.
+ // See the Windows SDK documentation for more information on
+ // WaitForMultipleObjects().
+ const DWORD UNBLOCK = WAIT_OBJECT_0;
+ const DWORD STOP = WAIT_OBJECT_0 + 1;
+
+ HANDLE ahWaitEvents[] = { m_hUnblockOutputPinEvent, m_hStopEvent };
+ DWORD dwNumWaitEvents = sizeof(ahWaitEvents)/sizeof(HANDLE);
+
+ DWORD dwReturnValue = ::WaitForMultipleObjects( dwNumWaitEvents, ahWaitEvents, FALSE, INFINITE );
+
+ m_BlockStateLock.Lock();
+
+ #ifdef DEBUG
+ AssertValid();
+ #endif // DEBUG
+
+ switch( dwReturnValue ) {
+ case UNBLOCK:
+ break;
+
+ case STOP:
+ return VFW_E_STATE_CHANGED;
+
+ case WAIT_FAILED:
+ return AmGetLastErrorToHResult();
+
+ default:
+ DbgBreak( "An Unexpected case occured in CDynamicOutputPin::StartUsingOutputPin()." );
+ return E_UNEXPECTED;
+ }
+ }
+
+ m_dwNumOutstandingOutputPinUsers++;
+
+ #ifdef DEBUG
+ AssertValid();
+ #endif // DEBUG
+
+ return S_OK;
+}
+
+void CDynamicOutputPin::StopUsingOutputPin(void)
+{
+ CAutoLock alBlockStateLock(&m_BlockStateLock);
+
+ #ifdef DEBUG
+ AssertValid();
+ #endif // DEBUG
+
+ m_dwNumOutstandingOutputPinUsers--;
+
+ if((m_dwNumOutstandingOutputPinUsers == 0) && (NOT_BLOCKED != m_BlockState)) {
+ BlockOutputPin();
+ }
+
+ #ifdef DEBUG
+ AssertValid();
+ #endif // DEBUG
+}
+
+bool CDynamicOutputPin::StreamingThreadUsingOutputPin(void)
+{
+ CAutoLock alBlockStateLock(&m_BlockStateLock);
+
+ return (m_dwNumOutstandingOutputPinUsers > 0);
+}
+
+void CDynamicOutputPin::SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent)
+{
+ // This pointer is not addrefed because filters are not allowed to
+ // hold references to the filter graph manager. See the documentation for
+ // IBaseFilter::JoinFilterGraph() in the Direct Show SDK for more information.
+ m_pGraphConfig = pGraphConfig;
+
+ m_hStopEvent = hStopEvent;
+}
+
+HRESULT CDynamicOutputPin::Active(void)
+{
+ // Make sure the user initialized the object by calling SetConfigInfo().
+ if((NULL == m_hStopEvent) || (NULL == m_pGraphConfig)) {
+ DbgBreak( ERROR: CDynamicOutputPin::Active() failed because m_pGraphConfig and m_hStopEvent were not initialized. Call SetConfigInfo() to initialize them. );
+ return E_FAIL;
+ }
+
+ // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
+ // The ASSERT can also fire if the event if destroyed and then Active() is called. An event
+ // handle is invalid if 1) the event does not exist or the user does not have the security
+ // permissions to use the event.
+ EXECUTE_ASSERT(ResetEvent(m_hStopEvent));
+
+ return CBaseOutputPin::Active();
+}
+
+HRESULT CDynamicOutputPin::Inactive(void)
+{
+ // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
+ // The ASSERT can also fire if the event if destroyed and then Active() is called. An event
+ // handle is invalid if 1) the event does not exist or the user does not have the security
+ // permissions to use the event.
+ EXECUTE_ASSERT(SetEvent(m_hStopEvent));
+
+ return CBaseOutputPin::Inactive();
+}
+
+HRESULT CDynamicOutputPin::DeliverBeginFlush(void)
+{
+ // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
+ // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.
+ // An event handle is invalid if 1) the event does not exist or the user does not have the security
+ // permissions to use the event.
+ EXECUTE_ASSERT(SetEvent(m_hStopEvent));
+
+ return CBaseOutputPin::DeliverBeginFlush();
+}
+
+HRESULT CDynamicOutputPin::DeliverEndFlush(void)
+{
+ // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
+ // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.
+ // An event handle is invalid if 1) the event does not exist or the user does not have the security
+ // permissions to use the event.
+ EXECUTE_ASSERT(ResetEvent(m_hStopEvent));
+
+ return CBaseOutputPin::DeliverEndFlush();
+}
+
+
+// ChangeOutputFormat() either dynamicly changes the connection's format type or it dynamicly
+// reconnects the output pin.
+HRESULT CDynamicOutputPin::ChangeOutputFormat
+ (
+ const AM_MEDIA_TYPE *pmt,
+ REFERENCE_TIME tSegmentStart,
+ REFERENCE_TIME tSegmentStop,
+ double dSegmentRate
+ )
+{
+ // The caller should call StartUsingOutputPin() before calling this
+ // method.
+ ASSERT(StreamingThreadUsingOutputPin());
+
+ // Callers should always pass a valid media type to ChangeOutputFormat() .
+ ASSERT(NULL != pmt);
+
+ CMediaType cmt(*pmt);
+ HRESULT hr = ChangeMediaType(&cmt);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = DeliverNewSegment(tSegmentStart, tSegmentStop, dSegmentRate);
+ if( FAILED( hr ) ) {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+HRESULT CDynamicOutputPin::ChangeMediaType(const CMediaType *pmt)
+{
+ // The caller should call StartUsingOutputPin() before calling this
+ // method.
+ ASSERT(StreamingThreadUsingOutputPin());
+
+ // This function assumes the filter graph is running.
+ ASSERT(!IsStopped());
+
+ if(!IsConnected()) {
+ return VFW_E_NOT_CONNECTED;
+ }
+
+ /* First check if the downstream pin will accept a dynamic
+ format change
+ */
+ QzCComPtr pConnection;
+
+ m_Connected->QueryInterface(IID_IPinConnection, (void **)&pConnection);
+ if(pConnection != NULL) {
+
+ if(S_OK == pConnection->DynamicQueryAccept(pmt)) {
+
+ HRESULT hr = ChangeMediaTypeHelper(pmt);
+ if(FAILED(hr)) {
+ return hr;
+ }
+
+ return S_OK;
+ }
+ }
+
+ /* Can't do the dynamic connection */
+ return DynamicReconnect(pmt);
+}
+
+HRESULT CDynamicOutputPin::ChangeMediaTypeHelper(const CMediaType *pmt)
+{
+ // The caller should call StartUsingOutputPin() before calling this
+ // method.
+ ASSERT(StreamingThreadUsingOutputPin());
+
+ HRESULT hr = m_Connected->ReceiveConnection(this, pmt);
+ if(FAILED(hr)) {
+ return hr;
+ }
+
+ hr = SetMediaType(pmt);
+ if(FAILED(hr)) {
+ return hr;
+ }
+
+ // Does this pin use the local memory transport?
+ if(NULL != m_pInputPin) {
+ // This function assumes that m_pInputPin and m_Connected are
+ // two different interfaces to the same object.
+ ASSERT(::IsEqualObject(m_Connected, m_pInputPin));
+
+ ALLOCATOR_PROPERTIES apInputPinRequirements;
+ apInputPinRequirements.cbAlign = 0;
+ apInputPinRequirements.cbBuffer = 0;
+ apInputPinRequirements.cbPrefix = 0;
+ apInputPinRequirements.cBuffers = 0;
+
+ m_pInputPin->GetAllocatorRequirements(&apInputPinRequirements);
+
+ // A zero allignment does not make any sense.
+ if(0 == apInputPinRequirements.cbAlign) {
+ apInputPinRequirements.cbAlign = 1;
+ }
+
+ hr = m_pAllocator->Decommit();
+ if(FAILED(hr)) {
+ return hr;
+ }
+
+ hr = DecideBufferSize(m_pAllocator, &apInputPinRequirements);
+ if(FAILED(hr)) {
+ return hr;
+ }
+
+ hr = m_pAllocator->Commit();
+ if(FAILED(hr)) {
+ return hr;
+ }
+
+ hr = m_pInputPin->NotifyAllocator(m_pAllocator, m_bPinUsesReadOnlyAllocator);
+ if(FAILED(hr)) {
+ return hr;
+ }
+ }
+
+ return S_OK;
+}
+
+// this method has to be called from the thread that is pushing data,
+// and it's the caller's responsibility to make sure that the thread
+// has no outstand samples because they cannot be delivered after a
+// reconnect
+//
+HRESULT CDynamicOutputPin::DynamicReconnect( const CMediaType* pmt )
+{
+ // The caller should call StartUsingOutputPin() before calling this
+ // method.
+ ASSERT(StreamingThreadUsingOutputPin());
+
+ if((m_pGraphConfig == NULL) || (NULL == m_hStopEvent)) {
+ return E_FAIL;
+ }
+
+ HRESULT hr = m_pGraphConfig->Reconnect(
+ this,
+ NULL,
+ pmt,
+ NULL,
+ m_hStopEvent,
+ AM_GRAPH_CONFIG_RECONNECT_CACHE_REMOVED_FILTERS );
+
+ return hr;
+}
+
+HRESULT CDynamicOutputPin::CompleteConnect(IPin *pReceivePin)
+{
+ HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);
+ if(SUCCEEDED(hr)) {
+ if(!IsStopped() && m_pAllocator) {
+ hr = m_pAllocator->Commit();
+ ASSERT(hr != VFW_E_ALREADY_COMMITTED);
+ }
+ }
+
+ return hr;
+}
+
+#ifdef DEBUG
+void CDynamicOutputPin::AssertValid(void)
+{
+ // Make sure the object was correctly initialized.
+
+ // This ASSERT only fires if the object failed to initialize
+ // and the user ignored the constructor's return code (phr).
+ ASSERT(NULL != m_hUnblockOutputPinEvent);
+
+ // If either of these ASSERTs fire, the user did not correctly call
+ // SetConfigInfo().
+ ASSERT(NULL != m_hStopEvent);
+ ASSERT(NULL != m_pGraphConfig);
+
+ // Make sure the block state is consistent.
+
+ CAutoLock alBlockStateLock(&m_BlockStateLock);
+
+ // BLOCK_STATE variables only have three legal values: PENDING, BLOCKED and NOT_BLOCKED.
+ ASSERT((NOT_BLOCKED == m_BlockState) || (PENDING == m_BlockState) || (BLOCKED == m_BlockState));
+
+ // m_hNotifyCallerPinBlockedEvent is only needed when a block operation cannot complete
+ // immediately.
+ ASSERT(((NULL == m_hNotifyCallerPinBlockedEvent) && (PENDING != m_BlockState)) ||
+ ((NULL != m_hNotifyCallerPinBlockedEvent) && (PENDING == m_BlockState)) );
+
+ // m_dwBlockCallerThreadID should always be 0 if the pin is not blocked and
+ // the user is not trying to block the pin.
+ ASSERT((0 == m_dwBlockCallerThreadID) || (NOT_BLOCKED != m_BlockState));
+
+ // If this ASSERT fires, the streaming thread is using the output pin and the
+ // output pin is blocked.
+ ASSERT(((0 != m_dwNumOutstandingOutputPinUsers) && (BLOCKED != m_BlockState)) ||
+ ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED != m_BlockState)) ||
+ ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED == m_BlockState)) );
+}
+#endif // DEBUG
+
+HRESULT CDynamicOutputPin::WaitEvent(HANDLE hEvent)
+{
+ const DWORD EVENT_SIGNALED = WAIT_OBJECT_0;
+
+ DWORD dwReturnValue = ::WaitForSingleObject(hEvent, INFINITE);
+
+ switch( dwReturnValue ) {
+ case EVENT_SIGNALED:
+ return S_OK;
+
+ case WAIT_FAILED:
+ return AmGetLastErrorToHResult();
+
+ default:
+ DbgBreak( "An Unexpected case occured in CDynamicOutputPin::WaitEvent()." );
+ return E_UNEXPECTED;
+ }
+}
+
+//=====================================================================
+//=====================================================================
+// Implements CBaseAllocator
+//=====================================================================
+//=====================================================================
+
+
+/* Constructor overrides the default settings for the free list to request
+ that it be alertable (ie the list can be cast to a handle which can be
+ passed to WaitForSingleObject). Both of the allocator lists also ask for
+ object locking, the all list matches the object default settings but I
+ have included them here just so it is obvious what kind of list it is */
+
+CBaseAllocator::CBaseAllocator(__in_opt LPCTSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ __inout HRESULT *phr,
+ BOOL bEvent,
+ BOOL fEnableReleaseCallback
+ ) :
+ CUnknown(pName, pUnk),
+ m_lAllocated(0),
+ m_bChanged(FALSE),
+ m_bCommitted(FALSE),
+ m_bDecommitInProgress(FALSE),
+ m_lSize(0),
+ m_lCount(0),
+ m_lAlignment(0),
+ m_lPrefix(0),
+ m_hSem(NULL),
+ m_lWaiting(0),
+ m_fEnableReleaseCallback(fEnableReleaseCallback),
+ m_pNotify(NULL)
+{
+#ifdef DXMPERF
+ PERFLOG_CTOR( pName ? pName : L"CBaseAllocator", (IMemAllocator *) this );
+#endif // DXMPERF
+
+ if (bEvent) {
+ m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
+ if (m_hSem == NULL) {
+ *phr = E_OUTOFMEMORY;
+ return;
+ }
+ }
+}
+
+#ifdef UNICODE
+CBaseAllocator::CBaseAllocator(__in_opt LPCSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ __inout HRESULT *phr,
+ BOOL bEvent,
+ BOOL fEnableReleaseCallback) :
+ CUnknown(pName, pUnk),
+ m_lAllocated(0),
+ m_bChanged(FALSE),
+ m_bCommitted(FALSE),
+ m_bDecommitInProgress(FALSE),
+ m_lSize(0),
+ m_lCount(0),
+ m_lAlignment(0),
+ m_lPrefix(0),
+ m_hSem(NULL),
+ m_lWaiting(0),
+ m_fEnableReleaseCallback(fEnableReleaseCallback),
+ m_pNotify(NULL)
+{
+#ifdef DXMPERF
+ PERFLOG_CTOR( L"CBaseAllocator", (IMemAllocator *) this );
+#endif // DXMPERF
+
+ if (bEvent) {
+ m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
+ if (m_hSem == NULL) {
+ *phr = E_OUTOFMEMORY;
+ return;
+ }
+ }
+}
+#endif
+
+/* Destructor */
+
+CBaseAllocator::~CBaseAllocator()
+{
+ // we can't call Decommit here since that would mean a call to a
+ // pure virtual in destructor.
+ // We must assume that the derived class has gone into decommit state in
+ // its destructor.
+#ifdef DXMPERF
+ PERFLOG_DTOR( L"CBaseAllocator", (IMemAllocator *) this );
+#endif // DXMPERF
+
+ ASSERT(!m_bCommitted);
+ if (m_hSem != NULL) {
+ EXECUTE_ASSERT(CloseHandle(m_hSem));
+ }
+ if (m_pNotify) {
+ m_pNotify->Release();
+ }
+}
+
+
+/* Override this to publicise our interfaces */
+
+STDMETHODIMP
+CBaseAllocator::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ /* Do we know about this interface */
+
+ if (riid == IID_IMemAllocator ||
+ riid == IID_IMemAllocatorCallbackTemp && m_fEnableReleaseCallback) {
+ return GetInterface((IMemAllocatorCallbackTemp *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+/* This sets the size and count of the required samples. The memory isn't
+ actually allocated until Commit() is called, if memory has already been
+ allocated then assuming no samples are outstanding the user may call us
+ to change the buffering, the memory will be released in Commit() */
+
+STDMETHODIMP
+CBaseAllocator::SetProperties(
+ __in ALLOCATOR_PROPERTIES* pRequest,
+ __out ALLOCATOR_PROPERTIES* pActual)
+{
+ CheckPointer(pRequest, E_POINTER);
+ CheckPointer(pActual, E_POINTER);
+ ValidateReadWritePtr(pActual, sizeof(ALLOCATOR_PROPERTIES));
+ CAutoLock cObjectLock(this);
+
+ ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));
+
+ ASSERT(pRequest->cbBuffer > 0);
+
+ /* Check the alignment requested */
+ if (pRequest->cbAlign != 1) {
+ DbgLog((LOG_ERROR, 2, TEXT("Alignment requested was 0x%x, not 1"),
+ pRequest->cbAlign));
+ return VFW_E_BADALIGN;
+ }
+
+ /* Can't do this if already committed, there is an argument that says we
+ should not reject the SetProperties call if there are buffers still
+ active. However this is called by the source filter, which is the same
+ person who is holding the samples. Therefore it is not unreasonable
+ for them to free all their samples before changing the requirements */
+
+ if (m_bCommitted) {
+ return VFW_E_ALREADY_COMMITTED;
+ }
+
+ /* Must be no outstanding buffers */
+
+ if (m_lAllocated != m_lFree.GetCount()) {
+ return VFW_E_BUFFERS_OUTSTANDING;
+ }
+
+ /* There isn't any real need to check the parameters as they
+ will just be rejected when the user finally calls Commit */
+
+ pActual->cbBuffer = m_lSize = pRequest->cbBuffer;
+ pActual->cBuffers = m_lCount = pRequest->cBuffers;
+ pActual->cbAlign = m_lAlignment = pRequest->cbAlign;
+ pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;
+
+ m_bChanged = TRUE;
+ return NOERROR;
+}
+
+STDMETHODIMP
+CBaseAllocator::GetProperties(
+ __out ALLOCATOR_PROPERTIES * pActual)
+{
+ CheckPointer(pActual,E_POINTER);
+ ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));
+
+ CAutoLock cObjectLock(this);
+ pActual->cbBuffer = m_lSize;
+ pActual->cBuffers = m_lCount;
+ pActual->cbAlign = m_lAlignment;
+ pActual->cbPrefix = m_lPrefix;
+ return NOERROR;
+}
+
+// get container for a sample. Blocking, synchronous call to get the
+// next free buffer (as represented by an IMediaSample interface).
+// on return, the time etc properties will be invalid, but the buffer
+// pointer and size will be correct.
+
+HRESULT CBaseAllocator::GetBuffer(__deref_out IMediaSample **ppBuffer,
+ __in_opt REFERENCE_TIME *pStartTime,
+ __in_opt REFERENCE_TIME *pEndTime,
+ DWORD dwFlags
+ )
+{
+ UNREFERENCED_PARAMETER(pStartTime);
+ UNREFERENCED_PARAMETER(pEndTime);
+ UNREFERENCED_PARAMETER(dwFlags);
+ CMediaSample *pSample;
+
+ *ppBuffer = NULL;
+ for (;;)
+ {
+ { // scope for lock
+ CAutoLock cObjectLock(this);
+
+ /* Check we are committed */
+ if (!m_bCommitted) {
+ return VFW_E_NOT_COMMITTED;
+ }
+ pSample = (CMediaSample *) m_lFree.RemoveHead();
+ if (pSample == NULL) {
+ SetWaiting();
+ }
+ }
+
+ /* If we didn't get a sample then wait for the list to signal */
+
+ if (pSample) {
+ break;
+ }
+ if (dwFlags & AM_GBF_NOWAIT) {
+ return VFW_E_TIMEOUT;
+ }
+ ASSERT(m_hSem != NULL);
+ WaitForSingleObject(m_hSem, INFINITE);
+ }
+
+ /* Addref the buffer up to one. On release
+ back to zero instead of being deleted, it will requeue itself by
+ calling the ReleaseBuffer member function. NOTE the owner of a
+ media sample must always be derived from CBaseAllocator */
+
+
+ ASSERT(pSample->m_cRef == 0);
+ pSample->m_cRef = 1;
+ *ppBuffer = pSample;
+
+#ifdef DXMPERF
+ PERFLOG_GETBUFFER( (IMemAllocator *) this, pSample );
+#endif // DXMPERF
+
+ return NOERROR;
+}
+
+
+/* Final release of a CMediaSample will call this */
+
+STDMETHODIMP
+CBaseAllocator::ReleaseBuffer(IMediaSample * pSample)
+{
+ CheckPointer(pSample,E_POINTER);
+ ValidateReadPtr(pSample,sizeof(IMediaSample));
+
+#ifdef DXMPERF
+ PERFLOG_RELBUFFER( (IMemAllocator *) this, pSample );
+#endif // DXMPERF
+
+
+ BOOL bRelease = FALSE;
+ {
+ CAutoLock cal(this);
+
+ /* Put back on the free list */
+
+ m_lFree.Add((CMediaSample *)pSample);
+ if (m_lWaiting != 0) {
+ NotifySample();
+ }
+
+ // if there is a pending Decommit, then we need to complete it by
+ // calling Free() when the last buffer is placed on the free list
+
+ LONG l1 = m_lFree.GetCount();
+ if (m_bDecommitInProgress && (l1 == m_lAllocated)) {
+ Free();
+ m_bDecommitInProgress = FALSE;
+ bRelease = TRUE;
+ }
+ }
+
+ if (m_pNotify) {
+
+ ASSERT(m_fEnableReleaseCallback);
+
+ //
+ // Note that this is not synchronized with setting up a notification
+ // method.
+ //
+ m_pNotify->NotifyRelease();
+ }
+
+ /* For each buffer there is one AddRef, made in GetBuffer and released
+ here. This may cause the allocator and all samples to be deleted */
+
+ if (bRelease) {
+ Release();
+ }
+ return NOERROR;
+}
+
+STDMETHODIMP
+CBaseAllocator::SetNotify(
+ IMemAllocatorNotifyCallbackTemp* pNotify
+ )
+{
+ ASSERT(m_fEnableReleaseCallback);
+ CAutoLock lck(this);
+ if (pNotify) {
+ pNotify->AddRef();
+ }
+ if (m_pNotify) {
+ m_pNotify->Release();
+ }
+ m_pNotify = pNotify;
+ return S_OK;
+}
+
+STDMETHODIMP
+CBaseAllocator::GetFreeCount(
+ __out LONG* plBuffersFree
+ )
+{
+ ASSERT(m_fEnableReleaseCallback);
+ CAutoLock cObjectLock(this);
+ *plBuffersFree = m_lCount - m_lAllocated + m_lFree.GetCount();
+ return NOERROR;
+}
+
+void
+CBaseAllocator::NotifySample()
+{
+ if (m_lWaiting != 0) {
+ ASSERT(m_hSem != NULL);
+ ReleaseSemaphore(m_hSem, m_lWaiting, 0);
+ m_lWaiting = 0;
+ }
+}
+
+STDMETHODIMP
+CBaseAllocator::Commit()
+{
+ /* Check we are not decommitted */
+ CAutoLock cObjectLock(this);
+
+ // cannot need to alloc or re-alloc if we are committed
+ if (m_bCommitted) {
+ return NOERROR;
+ }
+
+ /* Allow GetBuffer calls */
+
+ m_bCommitted = TRUE;
+
+ // is there a pending decommit ? if so, just cancel it
+ if (m_bDecommitInProgress) {
+ m_bDecommitInProgress = FALSE;
+
+ // don't call Alloc at this point. He cannot allow SetProperties
+ // between Decommit and the last free, so the buffer size cannot have
+ // changed. And because some of the buffers are not free yet, he
+ // cannot re-alloc anyway.
+ return NOERROR;
+ }
+
+ DbgLog((LOG_MEMORY, 1, TEXT("Allocating: %ldx%ld"), m_lCount, m_lSize));
+
+ // actually need to allocate the samples
+ HRESULT hr = Alloc();
+ if (FAILED(hr)) {
+ m_bCommitted = FALSE;
+ return hr;
+ }
+ AddRef();
+ return NOERROR;
+}
+
+
+STDMETHODIMP
+CBaseAllocator::Decommit()
+{
+ BOOL bRelease = FALSE;
+ {
+ /* Check we are not already decommitted */
+ CAutoLock cObjectLock(this);
+ if (m_bCommitted == FALSE) {
+ if (m_bDecommitInProgress == FALSE) {
+ return NOERROR;
+ }
+ }
+
+ /* No more GetBuffer calls will succeed */
+ m_bCommitted = FALSE;
+
+ // are any buffers outstanding?
+ if (m_lFree.GetCount() < m_lAllocated) {
+ // please complete the decommit when last buffer is freed
+ m_bDecommitInProgress = TRUE;
+ } else {
+ m_bDecommitInProgress = FALSE;
+
+ // need to complete the decommit here as there are no
+ // outstanding buffers
+
+ Free();
+ bRelease = TRUE;
+ }
+
+ // Tell anyone waiting that they can go now so we can
+ // reject their call
+#pragma warning(push)
+#ifndef _PREFAST_
+#pragma warning(disable:4068)
+#endif
+#pragma prefast(suppress:__WARNING_DEREF_NULL_PTR, "Suppress warning related to Free() invalidating 'this' which is no applicable to CBaseAllocator::Free()")
+ NotifySample();
+
+#pragma warning(pop)
+ }
+
+ if (bRelease) {
+ Release();
+ }
+ return NOERROR;
+}
+
+
+/* Base definition of allocation which checks we are ok to go ahead and do
+ the full allocation. We return S_FALSE if the requirements are the same */
+
+HRESULT
+CBaseAllocator::Alloc(void)
+{
+ /* Error if he hasn't set the size yet */
+ if (m_lCount <= 0 || m_lSize <= 0 || m_lAlignment <= 0) {
+ return VFW_E_SIZENOTSET;
+ }
+
+ /* should never get here while buffers outstanding */
+ ASSERT(m_lFree.GetCount() == m_lAllocated);
+
+ /* If the requirements haven't changed then don't reallocate */
+ if (m_bChanged == FALSE) {
+ return S_FALSE;
+ }
+
+ return NOERROR;
+}
+
+/* Implement CBaseAllocator::CSampleList::Remove(pSample)
+ Removes pSample from the list
+*/
+void
+CBaseAllocator::CSampleList::Remove(__inout CMediaSample * pSample)
+{
+ CMediaSample **pSearch;
+ for (pSearch = &m_List;
+ *pSearch != NULL;
+ pSearch = &(CBaseAllocator::NextSample(*pSearch))) {
+ if (*pSearch == pSample) {
+ *pSearch = CBaseAllocator::NextSample(pSample);
+ CBaseAllocator::NextSample(pSample) = NULL;
+ m_nOnList--;
+ return;
+ }
+ }
+ DbgBreak("Couldn't find sample in list");
+}
+
+//=====================================================================
+//=====================================================================
+// Implements CMemAllocator
+//=====================================================================
+//=====================================================================
+
+
+/* This goes in the factory template table to create new instances */
+CUnknown *CMemAllocator::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)
+{
+ CUnknown *pUnkRet = new CMemAllocator(NAME("CMemAllocator"), pUnk, phr);
+ return pUnkRet;
+}
+
+CMemAllocator::CMemAllocator(
+ __in_opt LPCTSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ __inout HRESULT *phr)
+ : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),
+ m_pBuffer(NULL)
+{
+}
+
+#ifdef UNICODE
+CMemAllocator::CMemAllocator(
+ __in_opt LPCSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ __inout HRESULT *phr)
+ : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),
+ m_pBuffer(NULL)
+{
+}
+#endif
+
+/* This sets the size and count of the required samples. The memory isn't
+ actually allocated until Commit() is called, if memory has already been
+ allocated then assuming no samples are outstanding the user may call us
+ to change the buffering, the memory will be released in Commit() */
+STDMETHODIMP
+CMemAllocator::SetProperties(
+ __in ALLOCATOR_PROPERTIES* pRequest,
+ __out ALLOCATOR_PROPERTIES* pActual)
+{
+ CheckPointer(pActual,E_POINTER);
+ ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));
+ CAutoLock cObjectLock(this);
+
+ ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));
+
+ ASSERT(pRequest->cbBuffer > 0);
+
+ SYSTEM_INFO SysInfo;
+ GetSystemInfo(&SysInfo);
+
+ /* Check the alignment request is a power of 2 */
+ if ((-pRequest->cbAlign & pRequest->cbAlign) != pRequest->cbAlign) {
+ DbgLog((LOG_ERROR, 1, TEXT("Alignment requested 0x%x not a power of 2!"),
+ pRequest->cbAlign));
+ }
+ /* Check the alignment requested */
+ if (pRequest->cbAlign == 0 ||
+ (SysInfo.dwAllocationGranularity & (pRequest->cbAlign - 1)) != 0) {
+ DbgLog((LOG_ERROR, 1, TEXT("Invalid alignment 0x%x requested - granularity = 0x%x"),
+ pRequest->cbAlign, SysInfo.dwAllocationGranularity));
+ return VFW_E_BADALIGN;
+ }
+
+ /* Can't do this if already committed, there is an argument that says we
+ should not reject the SetProperties call if there are buffers still
+ active. However this is called by the source filter, which is the same
+ person who is holding the samples. Therefore it is not unreasonable
+ for them to free all their samples before changing the requirements */
+
+ if (m_bCommitted == TRUE) {
+ return VFW_E_ALREADY_COMMITTED;
+ }
+
+ /* Must be no outstanding buffers */
+
+ if (m_lFree.GetCount() < m_lAllocated) {
+ return VFW_E_BUFFERS_OUTSTANDING;
+ }
+
+ /* There isn't any real need to check the parameters as they
+ will just be rejected when the user finally calls Commit */
+
+ // round length up to alignment - remember that prefix is included in
+ // the alignment
+ LONG lSize = pRequest->cbBuffer + pRequest->cbPrefix;
+ LONG lRemainder = lSize % pRequest->cbAlign;
+ if (lRemainder != 0) {
+ lSize = lSize - lRemainder + pRequest->cbAlign;
+ }
+ pActual->cbBuffer = m_lSize = (lSize - pRequest->cbPrefix);
+
+ pActual->cBuffers = m_lCount = pRequest->cBuffers;
+ pActual->cbAlign = m_lAlignment = pRequest->cbAlign;
+ pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;
+
+ m_bChanged = TRUE;
+ return NOERROR;
+}
+
+// override this to allocate our resources when Commit is called.
+//
+// note that our resources may be already allocated when this is called,
+// since we don't free them on Decommit. We will only be called when in
+// decommit state with all buffers free.
+//
+// object locked by caller
+HRESULT
+CMemAllocator::Alloc(void)
+{
+ CAutoLock lck(this);
+
+ /* Check he has called SetProperties */
+ HRESULT hr = CBaseAllocator::Alloc();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ /* If the requirements haven't changed then don't reallocate */
+ if (hr == S_FALSE) {
+ ASSERT(m_pBuffer);
+ return NOERROR;
+ }
+ ASSERT(hr == S_OK); // we use this fact in the loop below
+
+ /* Free the old resources */
+ if (m_pBuffer) {
+ ReallyFree();
+ }
+
+ /* Make sure we've got reasonable values */
+ if ( m_lSize < 0 || m_lPrefix < 0 || m_lCount < 0 ) {
+ return E_OUTOFMEMORY;
+ }
+
+ /* Compute the aligned size */
+ LONG lAlignedSize = m_lSize + m_lPrefix;
+
+ /* Check overflow */
+ if (lAlignedSize < m_lSize) {
+ return E_OUTOFMEMORY;
+ }
+
+ if (m_lAlignment > 1) {
+ LONG lRemainder = lAlignedSize % m_lAlignment;
+ if (lRemainder != 0) {
+ LONG lNewSize = lAlignedSize + m_lAlignment - lRemainder;
+ if (lNewSize < lAlignedSize) {
+ return E_OUTOFMEMORY;
+ }
+ lAlignedSize = lNewSize;
+ }
+ }
+
+ /* Create the contiguous memory block for the samples
+ making sure it's properly aligned (64K should be enough!)
+ */
+ ASSERT(lAlignedSize % m_lAlignment == 0);
+
+ LONGLONG lToAllocate = m_lCount * (LONGLONG)lAlignedSize;
+
+ /* Check overflow */
+ if (lToAllocate > MAXLONG) {
+ return E_OUTOFMEMORY;
+ }
+
+ m_pBuffer = (PBYTE)VirtualAlloc(NULL,
+ (LONG)lToAllocate,
+ MEM_COMMIT,
+ PAGE_READWRITE);
+
+ if (m_pBuffer == NULL) {
+ return E_OUTOFMEMORY;
+ }
+
+ LPBYTE pNext = m_pBuffer;
+ CMediaSample *pSample;
+
+ ASSERT(m_lAllocated == 0);
+
+ // Create the new samples - we have allocated m_lSize bytes for each sample
+ // plus m_lPrefix bytes per sample as a prefix. We set the pointer to
+ // the memory after the prefix - so that GetPointer() will return a pointer
+ // to m_lSize bytes.
+ for (; m_lAllocated < m_lCount; m_lAllocated++, pNext += lAlignedSize) {
+
+
+ pSample = new CMediaSample(
+ NAME("Default memory media sample"),
+ this,
+ &hr,
+ pNext + m_lPrefix, // GetPointer() value
+ m_lSize); // not including prefix
+
+ ASSERT(SUCCEEDED(hr));
+ if (pSample == NULL) {
+ return E_OUTOFMEMORY;
+ }
+
+ // This CANNOT fail
+ m_lFree.Add(pSample);
+ }
+
+ m_bChanged = FALSE;
+ return NOERROR;
+}
+
+
+// override this to free up any resources we have allocated.
+// called from the base class on Decommit when all buffers have been
+// returned to the free list.
+//
+// caller has already locked the object.
+
+// in our case, we keep the memory until we are deleted, so
+// we do nothing here. The memory is deleted in the destructor by
+// calling ReallyFree()
+void
+CMemAllocator::Free(void)
+{
+ return;
+}
+
+
+// called from the destructor (and from Alloc if changing size/count) to
+// actually free up the memory
+void
+CMemAllocator::ReallyFree(void)
+{
+ /* Should never be deleting this unless all buffers are freed */
+
+ ASSERT(m_lAllocated == m_lFree.GetCount());
+
+ /* Free up all the CMediaSamples */
+
+ CMediaSample *pSample;
+ for (;;) {
+ pSample = m_lFree.RemoveHead();
+ if (pSample != NULL) {
+ delete pSample;
+ } else {
+ break;
+ }
+ }
+
+ m_lAllocated = 0;
+
+ // free the block of buffer memory
+ if (m_pBuffer) {
+ EXECUTE_ASSERT(VirtualFree(m_pBuffer, 0, MEM_RELEASE));
+ m_pBuffer = NULL;
+ }
+}
+
+
+/* Destructor frees our memory resources */
+
+CMemAllocator::~CMemAllocator()
+{
+ Decommit();
+ ReallyFree();
+}
+
+// ------------------------------------------------------------------------
+// filter registration through IFilterMapper. used if IFilterMapper is
+// not found (Quartz 1.0 install)
+
+STDAPI
+AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata
+ , IFilterMapper * pIFM
+ , BOOL bRegister )
+{
+ DbgLog((LOG_TRACE, 3, TEXT("= AMovieSetupRegisterFilter")));
+
+ // check we've got data
+ //
+ if( NULL == psetupdata ) return S_FALSE;
+
+
+ // unregister filter
+ // (as pins are subkeys of filter's CLSID key
+ // they do not need to be removed separately).
+ //
+ DbgLog((LOG_TRACE, 3, TEXT("= = unregister filter")));
+ HRESULT hr = pIFM->UnregisterFilter( *(psetupdata->clsID) );
+
+
+ if( bRegister )
+ {
+ // register filter
+ //
+ DbgLog((LOG_TRACE, 3, TEXT("= = register filter")));
+ hr = pIFM->RegisterFilter( *(psetupdata->clsID)
+ , psetupdata->strName
+ , psetupdata->dwMerit );
+ if( SUCCEEDED(hr) )
+ {
+ // all its pins
+ //
+ DbgLog((LOG_TRACE, 3, TEXT("= = register filter pins")));
+ for( UINT m1=0; m1 < psetupdata->nPins; m1++ )
+ {
+ hr = pIFM->RegisterPin( *(psetupdata->clsID)
+ , psetupdata->lpPin[m1].strName
+ , psetupdata->lpPin[m1].bRendered
+ , psetupdata->lpPin[m1].bOutput
+ , psetupdata->lpPin[m1].bZero
+ , psetupdata->lpPin[m1].bMany
+ , *(psetupdata->lpPin[m1].clsConnectsToFilter)
+ , psetupdata->lpPin[m1].strConnectsToPin );
+
+ if( SUCCEEDED(hr) )
+ {
+ // and each pin's media types
+ //
+ DbgLog((LOG_TRACE, 3, TEXT("= = register filter pin types")));
+ for( UINT m2=0; m2 < psetupdata->lpPin[m1].nMediaTypes; m2++ )
+ {
+ hr = pIFM->RegisterPinType( *(psetupdata->clsID)
+ , psetupdata->lpPin[m1].strName
+ , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMajorType)
+ , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMinorType) );
+ if( FAILED(hr) ) break;
+ }
+ if( FAILED(hr) ) break;
+ }
+ if( FAILED(hr) ) break;
+ }
+ }
+ }
+
+ // handle one acceptable "error" - that
+ // of filter not being registered!
+ // (couldn't find a suitable #define'd
+ // name for the error!)
+ //
+ if( 0x80070002 == hr)
+ return NOERROR;
+ else
+ return hr;
+}
+
+// Remove warnings about unreferenced inline functions
+#pragma warning(disable:4514)
+
diff --git a/dshow_base/amfilter.h b/dshow_base/amfilter.h
index 70dbb44..14f17cd 100644
--- a/dshow_base/amfilter.h
+++ b/dshow_base/amfilter.h
@@ -1,1587 +1,1587 @@
-//------------------------------------------------------------------------------
-// File: AMFilter.h
-//
-// Desc: DirectShow base classes - efines class hierarchy for streams
-// architecture.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __FILTER__
-#define __FILTER__
-
-/* The following classes are declared in this header: */
-
-class CBaseMediaFilter; // IMediaFilter support
-class CBaseFilter; // IBaseFilter,IMediaFilter support
-class CBasePin; // Abstract base class for IPin interface
-class CEnumPins; // Enumerate input and output pins
-class CEnumMediaTypes; // Enumerate the pin's preferred formats
-class CBaseOutputPin; // Adds data provider member functions
-class CBaseInputPin; // Implements IMemInputPin interface
-class CMediaSample; // Basic transport unit for IMemInputPin
-class CBaseAllocator; // General list guff for most allocators
-class CMemAllocator; // Implements memory buffer allocation
-
-
-//=====================================================================
-//=====================================================================
-//
-// QueryFilterInfo and QueryPinInfo AddRef the interface pointers
-// they return. You can use the macro below to release the interface.
-//
-//=====================================================================
-//=====================================================================
-
-#define QueryFilterInfoReleaseGraph(fi) if ((fi).pGraph) (fi).pGraph->Release();
-
-#define QueryPinInfoReleaseFilter(pi) if ((pi).pFilter) (pi).pFilter->Release();
-
-//=====================================================================
-//=====================================================================
-// Defines CBaseMediaFilter
-//
-// Abstract base class implementing IMediaFilter.
-//
-// Typically you will derive your filter from CBaseFilter rather than
-// this, unless you are implementing an object such as a plug-in
-// distributor that needs to support IMediaFilter but not IBaseFilter.
-//
-// Note that IMediaFilter is derived from IPersist to allow query of
-// class id.
-//=====================================================================
-//=====================================================================
-
-class AM_NOVTABLE CBaseMediaFilter : public CUnknown,
- public IMediaFilter
-{
-
-protected:
-
- FILTER_STATE m_State; // current state: running, paused
- IReferenceClock *m_pClock; // this filter's reference clock
- // note: all filters in a filter graph use the same clock
-
- // offset from stream time to reference time
- CRefTime m_tStart;
-
- CLSID m_clsid; // This filters clsid
- // used for serialization
- CCritSec *m_pLock; // Object we use for locking
-
-public:
-
- CBaseMediaFilter(
- const TCHAR *pName,
- LPUNKNOWN pUnk,
- CCritSec *pLock,
- REFCLSID clsid);
-
- virtual ~CBaseMediaFilter();
-
- DECLARE_IUNKNOWN
-
- // override this to say what interfaces we support where
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
-
- //
- // --- IPersist method ---
- //
-
- STDMETHODIMP GetClassID(CLSID *pClsID);
-
- // --- IMediaFilter methods ---
-
- STDMETHODIMP GetState(DWORD dwMSecs, FILTER_STATE *State);
-
- STDMETHODIMP SetSyncSource(IReferenceClock *pClock);
-
- STDMETHODIMP GetSyncSource(IReferenceClock **pClock);
-
- // default implementation of Stop and Pause just record the
- // state. Override to activate or de-activate your filter.
- // Note that Run when called from Stopped state will call Pause
- // to ensure activation, so if you are a source or transform
- // you will probably not need to override Run.
- STDMETHODIMP Stop();
- STDMETHODIMP Pause();
-
-
- // the start parameter is the difference to be added to the
- // sample's stream time to get the reference time for
- // its presentation
- STDMETHODIMP Run(REFERENCE_TIME tStart);
-
- // --- helper methods ---
-
- // return the current stream time - ie find out what
- // stream time should be appearing now
- virtual HRESULT StreamTime(CRefTime& rtStream);
-
- // Is the filter currently active? (running or paused)
- BOOL IsActive() {
- CAutoLock cObjectLock(m_pLock);
- return ((m_State == State_Paused) || (m_State == State_Running));
- };
-};
-
-//=====================================================================
-//=====================================================================
-// Defines CBaseFilter
-//
-// An abstract class providing basic IBaseFilter support for pin
-// enumeration and filter information reading.
-//
-// We cannot derive from CBaseMediaFilter since methods in IMediaFilter
-// are also in IBaseFilter and would be ambiguous. Since much of the code
-// assumes that they derive from a class that has m_State and other state
-// directly available, we duplicate code from CBaseMediaFilter rather than
-// having a member variable.
-//
-// Derive your filter from this, or from a derived object such as
-// CTransformFilter.
-//=====================================================================
-//=====================================================================
-
-
-class AM_NOVTABLE CBaseFilter : public CUnknown, // Handles an IUnknown
- public IBaseFilter, // The Filter Interface
- public IAMovieSetup // For un/registration
-{
-
-friend class CBasePin;
-
-protected:
- FILTER_STATE m_State; // current state: running, paused
- IReferenceClock *m_pClock; // this graph's ref clock
- CRefTime m_tStart; // offset from stream time to reference time
- CLSID m_clsid; // This filters clsid
- // used for serialization
- CCritSec *m_pLock; // Object we use for locking
-
- WCHAR *m_pName; // Full filter name
- IFilterGraph *m_pGraph; // Graph we belong to
- IMediaEventSink *m_pSink; // Called with notify events
- LONG m_PinVersion; // Current pin version
-
-public:
-
- CBaseFilter(
- const TCHAR *pName, // Object description
- LPUNKNOWN pUnk, // IUnknown of delegating object
- CCritSec *pLock, // Object who maintains lock
- REFCLSID clsid); // The clsid to be used to serialize this filter
-
- CBaseFilter(
- TCHAR *pName, // Object description
- LPUNKNOWN pUnk, // IUnknown of delegating object
- CCritSec *pLock, // Object who maintains lock
- REFCLSID clsid, // The clsid to be used to serialize this filter
- HRESULT *phr); // General OLE return code
-#ifdef UNICODE
- CBaseFilter(
- const CHAR *pName, // Object description
- LPUNKNOWN pUnk, // IUnknown of delegating object
- CCritSec *pLock, // Object who maintains lock
- REFCLSID clsid); // The clsid to be used to serialize this filter
-
- CBaseFilter(
- CHAR *pName, // Object description
- LPUNKNOWN pUnk, // IUnknown of delegating object
- CCritSec *pLock, // Object who maintains lock
- REFCLSID clsid, // The clsid to be used to serialize this filter
- HRESULT *phr); // General OLE return code
-#endif
- ~CBaseFilter();
-
- DECLARE_IUNKNOWN
-
- // override this to say what interfaces we support where
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
-#ifdef DEBUG
- STDMETHODIMP_(ULONG) NonDelegatingRelease();
-#endif
-
- //
- // --- IPersist method ---
- //
-
- STDMETHODIMP GetClassID(CLSID *pClsID);
-
- // --- IMediaFilter methods ---
-
- STDMETHODIMP GetState(DWORD dwMSecs, FILTER_STATE *State);
-
- STDMETHODIMP SetSyncSource(IReferenceClock *pClock);
-
- STDMETHODIMP GetSyncSource(IReferenceClock **pClock);
-
-
- // override Stop and Pause so we can activate the pins.
- // Note that Run will call Pause first if activation needed.
- // Override these if you want to activate your filter rather than
- // your pins.
- STDMETHODIMP Stop();
- STDMETHODIMP Pause();
-
- // the start parameter is the difference to be added to the
- // sample's stream time to get the reference time for
- // its presentation
- STDMETHODIMP Run(REFERENCE_TIME tStart);
-
- // --- helper methods ---
-
- // return the current stream time - ie find out what
- // stream time should be appearing now
- virtual HRESULT StreamTime(CRefTime& rtStream);
-
- // Is the filter currently active?
- BOOL IsActive() {
- CAutoLock cObjectLock(m_pLock);
- return ((m_State == State_Paused) || (m_State == State_Running));
- };
-
- // Is this filter stopped (without locking)
- BOOL IsStopped() {
- return (m_State == State_Stopped);
- };
-
- //
- // --- IBaseFilter methods ---
- //
-
- // pin enumerator
- STDMETHODIMP EnumPins(
- IEnumPins ** ppEnum);
-
-
- // default behaviour of FindPin assumes pin ids are their names
- STDMETHODIMP FindPin(
- LPCWSTR Id,
- IPin ** ppPin
- );
-
- STDMETHODIMP QueryFilterInfo(
- FILTER_INFO * pInfo);
-
- STDMETHODIMP JoinFilterGraph(
- IFilterGraph * pGraph,
- LPCWSTR pName);
-
- // return a Vendor information string. Optional - may return E_NOTIMPL.
- // memory returned should be freed using CoTaskMemFree
- // default implementation returns E_NOTIMPL
- STDMETHODIMP QueryVendorInfo(
- LPWSTR* pVendorInfo
- );
-
- // --- helper methods ---
-
- // send an event notification to the filter graph if we know about it.
- // returns S_OK if delivered, S_FALSE if the filter graph does not sink
- // events, or an error otherwise.
- HRESULT NotifyEvent(
- long EventCode,
- LONG_PTR EventParam1,
- LONG_PTR EventParam2);
-
- // return the filter graph we belong to
- IFilterGraph *GetFilterGraph() {
- return m_pGraph;
- }
-
- // Request reconnect
- // pPin is the pin to reconnect
- // pmt is the type to reconnect with - can be NULL
- // Calls ReconnectEx on the filter graph
- HRESULT ReconnectPin(IPin *pPin, AM_MEDIA_TYPE const *pmt);
-
- // find out the current pin version (used by enumerators)
- virtual LONG GetPinVersion();
- void IncrementPinVersion();
-
- // you need to supply these to access the pins from the enumerator
- // and for default Stop and Pause/Run activation.
- virtual int GetPinCount() PURE;
- virtual CBasePin *GetPin(int n) PURE;
-
- // --- IAMovieSetup methods ---
-
- STDMETHODIMP Register(); // ask filter to register itself
- STDMETHODIMP Unregister(); // and unregister itself
-
- // --- setup helper methods ---
- // (override to return filters setup data)
-
- virtual LPAMOVIESETUP_FILTER GetSetupData(){ return NULL; }
-
-};
-
-
-//=====================================================================
-//=====================================================================
-// Defines CBasePin
-//
-// Abstract class that supports the basics of IPin
-//=====================================================================
-//=====================================================================
-
-class AM_NOVTABLE CBasePin : public CUnknown, public IPin, public IQualityControl
-{
-
-protected:
-
- WCHAR * m_pName; // This pin's name
- IPin *m_Connected; // Pin we have connected to
- PIN_DIRECTION m_dir; // Direction of this pin
- CCritSec *m_pLock; // Object we use for locking
- bool m_bRunTimeError; // Run time error generated
- bool m_bCanReconnectWhenActive; // OK to reconnect when active
- bool m_bTryMyTypesFirst; // When connecting enumerate
- // this pin's types first
- CBaseFilter *m_pFilter; // Filter we were created by
- IQualityControl *m_pQSink; // Target for Quality messages
- LONG m_TypeVersion; // Holds current type version
- CMediaType m_mt; // Media type of connection
-
- CRefTime m_tStart; // time from NewSegment call
- CRefTime m_tStop; // time from NewSegment
- double m_dRate; // rate from NewSegment
-
-#ifdef DEBUG
- LONG m_cRef; // Ref count tracing
-#endif
-
- // displays pin connection information
-
-#ifdef DEBUG
- void DisplayPinInfo(IPin *pReceivePin);
- void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt);
-#else
- void DisplayPinInfo(IPin *pReceivePin) {};
- void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt) {};
-#endif
-
- // used to agree a media type for a pin connection
-
- // given a specific media type, attempt a connection (includes
- // checking that the type is acceptable to this pin)
- HRESULT
- AttemptConnection(
- IPin* pReceivePin, // connect to this pin
- const CMediaType* pmt // using this type
- );
-
- // try all the media types in this enumerator - for each that
- // we accept, try to connect using ReceiveConnection.
- HRESULT TryMediaTypes(
- IPin *pReceivePin, // connect to this pin
- const CMediaType *pmt, // proposed type from Connect
- IEnumMediaTypes *pEnum); // try this enumerator
-
- // establish a connection with a suitable mediatype. Needs to
- // propose a media type if the pmt pointer is null or partially
- // specified - use TryMediaTypes on both our and then the other pin's
- // enumerator until we find one that works.
- HRESULT AgreeMediaType(
- IPin *pReceivePin, // connect to this pin
- const CMediaType *pmt); // proposed type from Connect
-
-public:
-
- CBasePin(
- TCHAR *pObjectName, // Object description
- CBaseFilter *pFilter, // Owning filter who knows about pins
- CCritSec *pLock, // Object who implements the lock
- HRESULT *phr, // General OLE return code
- LPCWSTR pName, // Pin name for us
- PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT
-#ifdef UNICODE
- CBasePin(
- CHAR *pObjectName, // Object description
- CBaseFilter *pFilter, // Owning filter who knows about pins
- CCritSec *pLock, // Object who implements the lock
- HRESULT *phr, // General OLE return code
- LPCWSTR pName, // Pin name for us
- PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT
-#endif
- virtual ~CBasePin();
-
- DECLARE_IUNKNOWN
-
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
- STDMETHODIMP_(ULONG) NonDelegatingRelease();
- STDMETHODIMP_(ULONG) NonDelegatingAddRef();
-
- // --- IPin methods ---
-
- // take lead role in establishing a connection. Media type pointer
- // may be null, or may point to partially-specified mediatype
- // (subtype or format type may be GUID_NULL).
- STDMETHODIMP Connect(
- IPin * pReceivePin,
- const AM_MEDIA_TYPE *pmt // optional media type
- );
-
- // (passive) accept a connection from another pin
- STDMETHODIMP ReceiveConnection(
- IPin * pConnector, // this is the initiating connecting pin
- const AM_MEDIA_TYPE *pmt // this is the media type we will exchange
- );
-
- STDMETHODIMP Disconnect();
-
- STDMETHODIMP ConnectedTo(IPin **pPin);
-
- STDMETHODIMP ConnectionMediaType(AM_MEDIA_TYPE *pmt);
-
- STDMETHODIMP QueryPinInfo(
- PIN_INFO * pInfo
- );
-
- STDMETHODIMP QueryDirection(
- PIN_DIRECTION * pPinDir
- );
-
- STDMETHODIMP QueryId(
- LPWSTR * Id
- );
-
- // does the pin support this media type
- STDMETHODIMP QueryAccept(
- const AM_MEDIA_TYPE *pmt
- );
-
- // return an enumerator for this pins preferred media types
- STDMETHODIMP EnumMediaTypes(
- IEnumMediaTypes **ppEnum
- );
-
- // return an array of IPin* - the pins that this pin internally connects to
- // All pins put in the array must be AddReffed (but no others)
- // Errors: "Can't say" - FAIL, not enough slots - return S_FALSE
- // Default: return E_NOTIMPL
- // The filter graph will interpret NOT_IMPL as any input pin connects to
- // all visible output pins and vice versa.
- // apPin can be NULL if nPin==0 (not otherwise).
- STDMETHODIMP QueryInternalConnections(
- IPin* *apPin, // array of IPin*
- ULONG *nPin // on input, the number of slots
- // on output the number of pins
- ) { return E_NOTIMPL; }
-
- // Called when no more data will be sent
- STDMETHODIMP EndOfStream(void);
-
- // Begin/EndFlush still PURE
-
- // NewSegment notifies of the start/stop/rate applying to the data
- // about to be received. Default implementation records data and
- // returns S_OK.
- // Override this to pass downstream.
- STDMETHODIMP NewSegment(
- REFERENCE_TIME tStart,
- REFERENCE_TIME tStop,
- double dRate);
-
- //================================================================================
- // IQualityControl methods
- //================================================================================
-
- STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
-
- STDMETHODIMP SetSink(IQualityControl * piqc);
-
- // --- helper methods ---
-
- // Returns true if the pin is connected. false otherwise.
- BOOL IsConnected(void) {return (m_Connected != NULL); };
- // Return the pin this is connected to (if any)
- IPin * GetConnected() { return m_Connected; };
-
- // Check if our filter is currently stopped
- BOOL IsStopped() {
- return (m_pFilter->m_State == State_Stopped);
- };
-
- // find out the current type version (used by enumerators)
- virtual LONG GetMediaTypeVersion();
- void IncrementTypeVersion();
-
- // switch the pin to active (paused or running) mode
- // not an error to call this if already active
- virtual HRESULT Active(void);
-
- // switch the pin to inactive state - may already be inactive
- virtual HRESULT Inactive(void);
-
- // Notify of Run() from filter
- virtual HRESULT Run(REFERENCE_TIME tStart);
-
- // check if the pin can support this specific proposed type and format
- virtual HRESULT CheckMediaType(const CMediaType *) PURE;
-
- // set the connection to use this format (previously agreed)
- virtual HRESULT SetMediaType(const CMediaType *);
-
- // check that the connection is ok before verifying it
- // can be overridden eg to check what interfaces will be supported.
- virtual HRESULT CheckConnect(IPin *);
-
- // Set and release resources required for a connection
- virtual HRESULT BreakConnect();
- virtual HRESULT CompleteConnect(IPin *pReceivePin);
-
- // returns the preferred formats for a pin
- virtual HRESULT GetMediaType(int iPosition,CMediaType *pMediaType);
-
- // access to NewSegment values
- REFERENCE_TIME CurrentStopTime() {
- return m_tStop;
- }
- REFERENCE_TIME CurrentStartTime() {
- return m_tStart;
- }
- double CurrentRate() {
- return m_dRate;
- }
-
- // Access name
- LPWSTR Name() { return m_pName; };
-
- // Can reconnectwhen active?
- void SetReconnectWhenActive(bool bCanReconnect)
- {
- m_bCanReconnectWhenActive = bCanReconnect;
- }
-
- bool CanReconnectWhenActive()
- {
- return m_bCanReconnectWhenActive;
- }
-
-protected:
- STDMETHODIMP DisconnectInternal();
-};
-
-
-//=====================================================================
-//=====================================================================
-// Defines CEnumPins
-//
-// Pin enumerator class that works by calling CBaseFilter. This interface
-// is provided by CBaseFilter::EnumPins and calls GetPinCount() and
-// GetPin() to enumerate existing pins. Needs to be a separate object so
-// that it can be cloned (creating an existing object at the same
-// position in the enumeration)
-//
-//=====================================================================
-//=====================================================================
-
-class CEnumPins : public IEnumPins // The interface we support
-{
- int m_Position; // Current ordinal position
- int m_PinCount; // Number of pins available
- CBaseFilter *m_pFilter; // The filter who owns us
- LONG m_Version; // Pin version information
- LONG m_cRef;
-
- typedef CGenericList CPinList;
-
- CPinList m_PinCache; // These pointers have not been AddRef'ed and
- // so they should not be dereferenced. They are
- // merely kept to ID which pins have been enumerated.
-
-#ifdef DEBUG
- DWORD m_dwCookie;
-#endif
-
- /* If while we are retrieving a pin for example from the filter an error
- occurs we assume that our internal state is stale with respect to the
- filter (someone may have deleted all the pins). We can check before
- starting whether or not the operation is likely to fail by asking the
- filter what it's current version number is. If the filter has not
- overriden the GetPinVersion method then this will always match */
-
- BOOL AreWeOutOfSync() {
- return (m_pFilter->GetPinVersion() == m_Version ? FALSE : TRUE);
- };
-
- /* This method performs the same operations as Reset, except is does not clear
- the cache of pins already enumerated. */
-
- STDMETHODIMP Refresh();
-
-public:
-
- CEnumPins(
- CBaseFilter *pFilter,
- CEnumPins *pEnumPins);
-
- virtual ~CEnumPins();
-
- // IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
- STDMETHODIMP_(ULONG) AddRef();
- STDMETHODIMP_(ULONG) Release();
-
- // IEnumPins
- STDMETHODIMP Next(
- ULONG cPins, // place this many pins...
- IPin ** ppPins, // ...in this array of IPin*
- ULONG * pcFetched // actual count passed returned here
- );
-
- STDMETHODIMP Skip(ULONG cPins);
- STDMETHODIMP Reset();
- STDMETHODIMP Clone(IEnumPins **ppEnum);
-
-
-};
-
-
-//=====================================================================
-//=====================================================================
-// Defines CEnumMediaTypes
-//
-// Enumerates the preferred formats for input and output pins
-//=====================================================================
-//=====================================================================
-
-class CEnumMediaTypes : public IEnumMediaTypes // The interface we support
-{
- int m_Position; // Current ordinal position
- CBasePin *m_pPin; // The pin who owns us
- LONG m_Version; // Media type version value
- LONG m_cRef;
-#ifdef DEBUG
- DWORD m_dwCookie;
-#endif
-
- /* The media types a filter supports can be quite dynamic so we add to
- the general IEnumXXXX interface the ability to be signaled when they
- change via an event handle the connected filter supplies. Until the
- Reset method is called after the state changes all further calls to
- the enumerator (except Reset) will return E_UNEXPECTED error code */
-
- BOOL AreWeOutOfSync() {
- return (m_pPin->GetMediaTypeVersion() == m_Version ? FALSE : TRUE);
- };
-
-public:
-
- CEnumMediaTypes(
- CBasePin *pPin,
- CEnumMediaTypes *pEnumMediaTypes);
-
- virtual ~CEnumMediaTypes();
-
- // IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
- STDMETHODIMP_(ULONG) AddRef();
- STDMETHODIMP_(ULONG) Release();
-
- // IEnumMediaTypes
- STDMETHODIMP Next(
- ULONG cMediaTypes, // place this many pins...
- AM_MEDIA_TYPE ** ppMediaTypes, // ...in this array
- ULONG * pcFetched // actual count passed
- );
-
- STDMETHODIMP Skip(ULONG cMediaTypes);
- STDMETHODIMP Reset();
- STDMETHODIMP Clone(IEnumMediaTypes **ppEnum);
-};
-
-
-
-
-//=====================================================================
-//=====================================================================
-// Defines CBaseOutputPin
-//
-// class derived from CBasePin that can pass buffers to a connected pin
-// that supports IMemInputPin. Supports IPin.
-//
-// Derive your output pin from this.
-//
-//=====================================================================
-//=====================================================================
-
-class AM_NOVTABLE CBaseOutputPin : public CBasePin
-{
-
-protected:
-
- IMemAllocator *m_pAllocator;
- IMemInputPin *m_pInputPin; // interface on the downstreaminput pin
- // set up in CheckConnect when we connect.
-
-public:
-
- CBaseOutputPin(
- TCHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pName);
-#ifdef UNICODE
- CBaseOutputPin(
- CHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pName);
-#endif
- // override CompleteConnect() so we can negotiate an allocator
- virtual HRESULT CompleteConnect(IPin *pReceivePin);
-
- // negotiate the allocator and its buffer size/count and other properties
- // Calls DecideBufferSize to set properties
- virtual HRESULT DecideAllocator(IMemInputPin * pPin, IMemAllocator ** pAlloc);
-
- // override this to set the buffer size and count. Return an error
- // if the size/count is not to your liking.
- // The allocator properties passed in are those requested by the
- // input pin - use eg the alignment and prefix members if you have
- // no preference on these.
- virtual HRESULT DecideBufferSize(
- IMemAllocator * pAlloc,
- ALLOCATOR_PROPERTIES * ppropInputRequest
- ) PURE;
-
- // returns an empty sample buffer from the allocator
- virtual HRESULT GetDeliveryBuffer(IMediaSample ** ppSample,
- REFERENCE_TIME * pStartTime,
- REFERENCE_TIME * pEndTime,
- DWORD dwFlags);
-
- // deliver a filled-in sample to the connected input pin
- // note - you need to release it after calling this. The receiving
- // pin will addref the sample if it needs to hold it beyond the
- // call.
- virtual HRESULT Deliver(IMediaSample *);
-
- // override this to control the connection
- virtual HRESULT InitAllocator(IMemAllocator **ppAlloc);
- HRESULT CheckConnect(IPin *pPin);
- HRESULT BreakConnect();
-
- // override to call Commit and Decommit
- HRESULT Active(void);
- HRESULT Inactive(void);
-
- // we have a default handling of EndOfStream which is to return
- // an error, since this should be called on input pins only
- STDMETHODIMP EndOfStream(void);
-
- // called from elsewhere in our filter to pass EOS downstream to
- // our connected input pin
- virtual HRESULT DeliverEndOfStream(void);
-
- // same for Begin/EndFlush - we handle Begin/EndFlush since it
- // is an error on an output pin, and we have Deliver methods to
- // call the methods on the connected pin
- STDMETHODIMP BeginFlush(void);
- STDMETHODIMP EndFlush(void);
- virtual HRESULT DeliverBeginFlush(void);
- virtual HRESULT DeliverEndFlush(void);
-
- // deliver NewSegment to connected pin - you will need to
- // override this if you queue any data in your output pin.
- virtual HRESULT DeliverNewSegment(
- REFERENCE_TIME tStart,
- REFERENCE_TIME tStop,
- double dRate);
-
- //================================================================================
- // IQualityControl methods
- //================================================================================
-
- // All inherited from CBasePin and not overridden here.
- // STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
- // STDMETHODIMP SetSink(IQualityControl * piqc);
-};
-
-
-//=====================================================================
-//=====================================================================
-// Defines CBaseInputPin
-//
-// derive your standard input pin from this.
-// you need to supply GetMediaType and CheckConnect etc (see CBasePin),
-// and you need to supply Receive to do something more useful.
-//
-//=====================================================================
-//=====================================================================
-
-class AM_NOVTABLE CBaseInputPin : public CBasePin,
- public IMemInputPin
-{
-
-protected:
-
- IMemAllocator *m_pAllocator; // Default memory allocator
-
- // allocator is read-only, so received samples
- // cannot be modified (probably only relevant to in-place
- // transforms
- BYTE m_bReadOnly;
-
- // in flushing state (between BeginFlush and EndFlush)
- // if TRUE, all Receives are returned with S_FALSE
- BYTE m_bFlushing;
-
- // Sample properties - initalized in Receive
- AM_SAMPLE2_PROPERTIES m_SampleProps;
-
-public:
-
- CBaseInputPin(
- TCHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pName);
-#ifdef UNICODE
- CBaseInputPin(
- CHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pName);
-#endif
- virtual ~CBaseInputPin();
-
- DECLARE_IUNKNOWN
-
- // override this to publicise our interfaces
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- // return the allocator interface that this input pin
- // would like the output pin to use
- STDMETHODIMP GetAllocator(IMemAllocator ** ppAllocator);
-
- // tell the input pin which allocator the output pin is actually
- // going to use.
- STDMETHODIMP NotifyAllocator(
- IMemAllocator * pAllocator,
- BOOL bReadOnly);
-
- // do something with this media sample
- STDMETHODIMP Receive(IMediaSample *pSample);
-
- // do something with these media samples
- STDMETHODIMP ReceiveMultiple (
- IMediaSample **pSamples,
- long nSamples,
- long *nSamplesProcessed);
-
- // See if Receive() blocks
- STDMETHODIMP ReceiveCanBlock();
-
- // Default handling for BeginFlush - call at the beginning
- // of your implementation (makes sure that all Receive calls
- // fail). After calling this, you need to free any queued data
- // and then call downstream.
- STDMETHODIMP BeginFlush(void);
-
- // default handling for EndFlush - call at end of your implementation
- // - before calling this, ensure that there is no queued data and no thread
- // pushing any more without a further receive, then call downstream,
- // then call this method to clear the m_bFlushing flag and re-enable
- // receives
- STDMETHODIMP EndFlush(void);
-
- // this method is optional (can return E_NOTIMPL).
- // default implementation returns E_NOTIMPL. Override if you have
- // specific alignment or prefix needs, but could use an upstream
- // allocator
- STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES*pProps);
-
- // Release the pin's allocator.
- HRESULT BreakConnect();
-
- // helper method to check the read-only flag
- BOOL IsReadOnly() {
- return m_bReadOnly;
- };
-
- // helper method to see if we are flushing
- BOOL IsFlushing() {
- return m_bFlushing;
- };
-
- // Override this for checking whether it's OK to process samples
- // Also call this from EndOfStream.
- virtual HRESULT CheckStreaming();
-
- // Pass a Quality notification on to the appropriate sink
- HRESULT PassNotify(Quality& q);
-
-
- //================================================================================
- // IQualityControl methods (from CBasePin)
- //================================================================================
-
- STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
-
- // no need to override:
- // STDMETHODIMP SetSink(IQualityControl * piqc);
-
-
- // switch the pin to inactive state - may already be inactive
- virtual HRESULT Inactive(void);
-
- // Return sample properties pointer
- AM_SAMPLE2_PROPERTIES * SampleProps() {
- ASSERT(m_SampleProps.cbData != 0);
- return &m_SampleProps;
- }
-
-};
-
-///////////////////////////////////////////////////////////////////////////
-// CDynamicOutputPin
-//
-
-class CDynamicOutputPin : public CBaseOutputPin,
- public IPinFlowControl
-{
-public:
-#ifdef UNICODE
- CDynamicOutputPin(
- CHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pName);
-#endif
-
- CDynamicOutputPin(
- TCHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pName);
-
- ~CDynamicOutputPin();
-
- // IUnknown Methods
- DECLARE_IUNKNOWN
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- // IPin Methods
- STDMETHODIMP Disconnect(void);
-
- // IPinFlowControl Methods
- STDMETHODIMP Block(DWORD dwBlockFlags, HANDLE hEvent);
-
- // Set graph config info
- void SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent);
-
- #ifdef DEBUG
- virtual HRESULT Deliver(IMediaSample *pSample);
- virtual HRESULT DeliverEndOfStream(void);
- virtual HRESULT DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate);
- #endif // DEBUG
-
- HRESULT DeliverBeginFlush(void);
- HRESULT DeliverEndFlush(void);
-
- HRESULT Inactive(void);
- HRESULT Active(void);
- virtual HRESULT CompleteConnect(IPin *pReceivePin);
-
- virtual HRESULT StartUsingOutputPin(void);
- virtual void StopUsingOutputPin(void);
- virtual bool StreamingThreadUsingOutputPin(void);
-
- HRESULT ChangeOutputFormat
- (
- const AM_MEDIA_TYPE *pmt,
- REFERENCE_TIME tSegmentStart,
- REFERENCE_TIME tSegmentStop,
- double dSegmentRate
- );
- HRESULT ChangeMediaType(const CMediaType *pmt);
- HRESULT DynamicReconnect(const CMediaType *pmt);
-
-protected:
- HRESULT SynchronousBlockOutputPin(void);
- HRESULT AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent);
- HRESULT UnblockOutputPin(void);
-
- void BlockOutputPin(void);
- void ResetBlockState(void);
-
- static HRESULT WaitEvent(HANDLE hEvent);
-
- enum BLOCK_STATE
- {
- NOT_BLOCKED,
- PENDING,
- BLOCKED
- };
-
- // This lock should be held when the following class members are
- // being used: m_hNotifyCallerPinBlockedEvent, m_BlockState,
- // m_dwBlockCallerThreadID and m_dwNumOutstandingOutputPinUsers.
- CCritSec m_BlockStateLock;
-
- // This event should be signaled when the output pin is
- // not blocked. This is a manual reset event. For more
- // information on events, see the documentation for
- // CreateEvent() in the Windows SDK.
- HANDLE m_hUnblockOutputPinEvent;
-
- // This event will be signaled when block operation succeedes or
- // when the user cancels the block operation. The block operation
- // can be canceled by calling IPinFlowControl2::Block( 0, NULL )
- // while the block operation is pending.
- HANDLE m_hNotifyCallerPinBlockedEvent;
-
- // The state of the current block operation.
- BLOCK_STATE m_BlockState;
-
- // The ID of the thread which last called IPinFlowControl::Block().
- // For more information on thread IDs, see the documentation for
- // GetCurrentThreadID() in the Windows SDK.
- DWORD m_dwBlockCallerThreadID;
-
- // The number of times StartUsingOutputPin() has been sucessfully
- // called and a corresponding call to StopUsingOutputPin() has not
- // been made. When this variable is greater than 0, the streaming
- // thread is calling IPin::NewSegment(), IPin::EndOfStream(),
- // IMemInputPin::Receive() or IMemInputPin::ReceiveMultiple(). The
- // streaming thread could also be calling: DynamicReconnect(),
- // ChangeMediaType() or ChangeOutputFormat(). The output pin cannot
- // be blocked while the output pin is being used.
- DWORD m_dwNumOutstandingOutputPinUsers;
-
- // This event should be set when the IMediaFilter::Stop() is called.
- // This is a manual reset event. It is also set when the output pin
- // delivers a flush to the connected input pin.
- HANDLE m_hStopEvent;
- IGraphConfig* m_pGraphConfig;
-
- // TRUE if the output pin's allocator's samples are read only.
- // Otherwise FALSE. For more information, see the documentation
- // for IMemInputPin::NotifyAllocator().
- BOOL m_bPinUsesReadOnlyAllocator;
-
-private:
- HRESULT Initialize(void);
- HRESULT ChangeMediaTypeHelper(const CMediaType *pmt);
-
- #ifdef DEBUG
- void AssertValid(void);
- #endif // DEBUG
-};
-
-class CAutoUsingOutputPin
-{
-public:
- CAutoUsingOutputPin( CDynamicOutputPin* pOutputPin, HRESULT* phr );
- ~CAutoUsingOutputPin();
-
-private:
- CDynamicOutputPin* m_pOutputPin;
-};
-
-inline CAutoUsingOutputPin::CAutoUsingOutputPin( CDynamicOutputPin* pOutputPin, HRESULT* phr ) :
- m_pOutputPin(NULL)
-{
- // The caller should always pass in valid pointers.
- ASSERT( NULL != pOutputPin );
- ASSERT( NULL != phr );
-
- // Make sure the user initialized phr.
- ASSERT( S_OK == *phr );
-
- HRESULT hr = pOutputPin->StartUsingOutputPin();
- if( FAILED( hr ) )
- {
- *phr = hr;
- return;
- }
-
- m_pOutputPin = pOutputPin;
-}
-
-inline CAutoUsingOutputPin::~CAutoUsingOutputPin()
-{
- if( NULL != m_pOutputPin )
- {
- m_pOutputPin->StopUsingOutputPin();
- }
-}
-
-#ifdef DEBUG
-
-inline HRESULT CDynamicOutputPin::Deliver(IMediaSample *pSample)
-{
- // The caller should call StartUsingOutputPin() before calling this
- // method.
- ASSERT(StreamingThreadUsingOutputPin());
-
- return CBaseOutputPin::Deliver(pSample);
-}
-
-inline HRESULT CDynamicOutputPin::DeliverEndOfStream(void)
-{
- // The caller should call StartUsingOutputPin() before calling this
- // method.
- ASSERT( StreamingThreadUsingOutputPin() );
-
- return CBaseOutputPin::DeliverEndOfStream();
-}
-
-inline HRESULT CDynamicOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
-{
- // The caller should call StartUsingOutputPin() before calling this
- // method.
- ASSERT(StreamingThreadUsingOutputPin());
-
- return CBaseOutputPin::DeliverNewSegment(tStart, tStop, dRate);
-}
-
-#endif // DEBUG
-
-//=====================================================================
-//=====================================================================
-// Memory allocators
-//
-// the shared memory transport between pins requires the input pin
-// to provide a memory allocator that can provide sample objects. A
-// sample object supports the IMediaSample interface.
-//
-// CBaseAllocator handles the management of free and busy samples. It
-// allocates CMediaSample objects. CBaseAllocator is an abstract class:
-// in particular it has no method of initializing the list of free
-// samples. CMemAllocator is derived from CBaseAllocator and initializes
-// the list of samples using memory from the standard IMalloc interface.
-//
-// If you want your buffers to live in some special area of memory,
-// derive your allocator object from CBaseAllocator. If you derive your
-// IMemInputPin interface object from CBaseMemInputPin, you will get
-// CMemAllocator-based allocation etc for free and will just need to
-// supply the Receive handling, and media type / format negotiation.
-//=====================================================================
-//=====================================================================
-
-
-//=====================================================================
-//=====================================================================
-// Defines CMediaSample
-//
-// an object of this class supports IMediaSample and represents a buffer
-// for media data with some associated properties. Releasing it returns
-// it to a freelist managed by a CBaseAllocator derived object.
-//=====================================================================
-//=====================================================================
-
-class CMediaSample : public IMediaSample2 // The interface we support
-{
-
-protected:
-
- friend class CBaseAllocator;
-
- /* Values for dwFlags - these are used for backward compatiblity
- only now - use AM_SAMPLE_xxx
- */
- enum { Sample_SyncPoint = 0x01, /* Is this a sync point */
- Sample_Preroll = 0x02, /* Is this a preroll sample */
- Sample_Discontinuity = 0x04, /* Set if start of new segment */
- Sample_TypeChanged = 0x08, /* Has the type changed */
- Sample_TimeValid = 0x10, /* Set if time is valid */
- Sample_MediaTimeValid = 0x20, /* Is the media time valid */
- Sample_TimeDiscontinuity = 0x40, /* Time discontinuity */
- Sample_StopValid = 0x100, /* Stop time valid */
- Sample_ValidFlags = 0x1FF
- };
-
- /* Properties, the media sample class can be a container for a format
- change in which case we take a copy of a type through the SetMediaType
- interface function and then return it when GetMediaType is called. As
- we do no internal processing on it we leave it as a pointer */
-
- DWORD m_dwFlags; /* Flags for this sample */
- /* Type specific flags are packed
- into the top word
- */
- DWORD m_dwTypeSpecificFlags; /* Media type specific flags */
- LPBYTE m_pBuffer; /* Pointer to the complete buffer */
- LONG m_lActual; /* Length of data in this sample */
- LONG m_cbBuffer; /* Size of the buffer */
- CBaseAllocator *m_pAllocator; /* The allocator who owns us */
- CMediaSample *m_pNext; /* Chaining in free list */
- REFERENCE_TIME m_Start; /* Start sample time */
- REFERENCE_TIME m_End; /* End sample time */
- LONGLONG m_MediaStart; /* Real media start position */
- LONG m_MediaEnd; /* A difference to get the end */
- AM_MEDIA_TYPE *m_pMediaType; /* Media type change data */
- DWORD m_dwStreamId; /* Stream id */
-public:
- LONG m_cRef; /* Reference count */
-
-
-public:
-
- CMediaSample(
- TCHAR *pName,
- CBaseAllocator *pAllocator,
- HRESULT *phr,
- LPBYTE pBuffer = NULL,
- LONG length = 0);
-#ifdef UNICODE
- CMediaSample(
- CHAR *pName,
- CBaseAllocator *pAllocator,
- HRESULT *phr,
- LPBYTE pBuffer = NULL,
- LONG length = 0);
-#endif
-
- virtual ~CMediaSample();
-
- /* Note the media sample does not delegate to its owner */
-
- STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
- STDMETHODIMP_(ULONG) AddRef();
- STDMETHODIMP_(ULONG) Release();
-
- // set the buffer pointer and length. Used by allocators that
- // want variable sized pointers or pointers into already-read data.
- // This is only available through a CMediaSample* not an IMediaSample*
- // and so cannot be changed by clients.
- HRESULT SetPointer(BYTE * ptr, LONG cBytes);
-
- // Get me a read/write pointer to this buffer's memory.
- STDMETHODIMP GetPointer(BYTE ** ppBuffer);
-
- STDMETHODIMP_(LONG) GetSize(void);
-
- // get the stream time at which this sample should start and finish.
- STDMETHODIMP GetTime(
- REFERENCE_TIME * pTimeStart, // put time here
- REFERENCE_TIME * pTimeEnd
- );
-
- // Set the stream time at which this sample should start and finish.
- STDMETHODIMP SetTime(
- REFERENCE_TIME * pTimeStart, // put time here
- REFERENCE_TIME * pTimeEnd
- );
- STDMETHODIMP IsSyncPoint(void);
- STDMETHODIMP SetSyncPoint(BOOL bIsSyncPoint);
- STDMETHODIMP IsPreroll(void);
- STDMETHODIMP SetPreroll(BOOL bIsPreroll);
-
- STDMETHODIMP_(LONG) GetActualDataLength(void);
- STDMETHODIMP SetActualDataLength(LONG lActual);
-
- // these allow for limited format changes in band
-
- STDMETHODIMP GetMediaType(AM_MEDIA_TYPE **ppMediaType);
- STDMETHODIMP SetMediaType(AM_MEDIA_TYPE *pMediaType);
-
- // returns S_OK if there is a discontinuity in the data (this same is
- // not a continuation of the previous stream of data
- // - there has been a seek).
- STDMETHODIMP IsDiscontinuity(void);
- // set the discontinuity property - TRUE if this sample is not a
- // continuation, but a new sample after a seek.
- STDMETHODIMP SetDiscontinuity(BOOL bDiscontinuity);
-
- // get the media times for this sample
- STDMETHODIMP GetMediaTime(
- LONGLONG * pTimeStart,
- LONGLONG * pTimeEnd
- );
-
- // Set the media times for this sample
- STDMETHODIMP SetMediaTime(
- LONGLONG * pTimeStart,
- LONGLONG * pTimeEnd
- );
-
- // Set and get properties (IMediaSample2)
- STDMETHODIMP GetProperties(
- DWORD cbProperties,
- BYTE * pbProperties
- );
-
- STDMETHODIMP SetProperties(
- DWORD cbProperties,
- const BYTE * pbProperties
- );
-};
-
-
-//=====================================================================
-//=====================================================================
-// Defines CBaseAllocator
-//
-// Abstract base class that manages a list of media samples
-//
-// This class provides support for getting buffers from the free list,
-// including handling of commit and (asynchronous) decommit.
-//
-// Derive from this class and override the Alloc and Free functions to
-// allocate your CMediaSample (or derived) objects and add them to the
-// free list, preparing them as necessary.
-//=====================================================================
-//=====================================================================
-
-class AM_NOVTABLE CBaseAllocator : public CUnknown,// A non delegating IUnknown
- public IMemAllocatorCallbackTemp, // The interface we support
- public CCritSec // Provides object locking
-{
- class CSampleList;
- friend class CSampleList;
-
- /* Trick to get at protected member in CMediaSample */
- static CMediaSample * &NextSample(CMediaSample *pSample)
- {
- return pSample->m_pNext;
- };
-
- /* Mini list class for the free list */
- class CSampleList
- {
- public:
- CSampleList() : m_List(NULL), m_nOnList(0) {};
-#ifdef DEBUG
- ~CSampleList()
- {
- ASSERT(m_nOnList == 0);
- };
-#endif
- CMediaSample *Head() const { return m_List; };
- CMediaSample *Next(CMediaSample *pSample) const { return CBaseAllocator::NextSample(pSample); };
- int GetCount() const { return m_nOnList; };
- void Add(CMediaSample *pSample)
- {
- ASSERT(pSample != NULL);
- CBaseAllocator::NextSample(pSample) = m_List;
- m_List = pSample;
- m_nOnList++;
- };
- CMediaSample *RemoveHead()
- {
- CMediaSample *pSample = m_List;
- if (pSample != NULL) {
- m_List = CBaseAllocator::NextSample(m_List);
- m_nOnList--;
- }
- return pSample;
- };
- void Remove(CMediaSample *pSample);
-
- public:
- CMediaSample *m_List;
- int m_nOnList;
- };
-protected:
-
- CSampleList m_lFree; // Free list
-
- /* Note to overriders of CBaseAllocator.
-
- We use a lazy signalling mechanism for waiting for samples.
- This means we don't call the OS if no waits occur.
-
- In order to implement this:
-
- 1. When a new sample is added to m_lFree call NotifySample() which
- calls ReleaseSemaphore on m_hSem with a count of m_lWaiting and
- sets m_lWaiting to 0.
- This must all be done holding the allocator's critical section.
-
- 2. When waiting for a sample call SetWaiting() which increments
- m_lWaiting BEFORE leaving the allocator's critical section.
-
- 3. Actually wait by calling WaitForSingleObject(m_hSem, INFINITE)
- having left the allocator's critical section. The effect of
- this is to remove 1 from the semaphore's count. You MUST call
- this once having incremented m_lWaiting.
-
- The following are then true when the critical section is not held :
- (let nWaiting = number about to wait or waiting)
-
- (1) if (m_lFree.GetCount() != 0) then (m_lWaiting == 0)
- (2) m_lWaiting + Semaphore count == nWaiting
-
- We would deadlock if
- nWaiting != 0 &&
- m_lFree.GetCount() != 0 &&
- Semaphore count == 0
-
- But from (1) if m_lFree.GetCount() != 0 then m_lWaiting == 0 so
- from (2) Semaphore count == nWaiting (which is non-0) so the
- deadlock can't happen.
- */
-
- HANDLE m_hSem; // For signalling
- long m_lWaiting; // Waiting for a free element
- long m_lCount; // how many buffers we have agreed to provide
- long m_lAllocated; // how many buffers are currently allocated
- long m_lSize; // agreed size of each buffer
- long m_lAlignment; // agreed alignment
- long m_lPrefix; // agreed prefix (preceeds GetPointer() value)
- BOOL m_bChanged; // Have the buffer requirements changed
-
- // if true, we are decommitted and can't allocate memory
- BOOL m_bCommitted;
- // if true, the decommit has happened, but we haven't called Free yet
- // as there are still outstanding buffers
- BOOL m_bDecommitInProgress;
-
- // Notification interface
- IMemAllocatorNotifyCallbackTemp *m_pNotify;
-
- BOOL m_fEnableReleaseCallback;
-
- // called to decommit the memory when the last buffer is freed
- // pure virtual - need to override this
- virtual void Free(void) PURE;
-
- // override to allocate the memory when commit called
- virtual HRESULT Alloc(void);
-
-public:
-
- CBaseAllocator(
- TCHAR *, LPUNKNOWN, HRESULT *,
- BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE);
-#ifdef UNICODE
- CBaseAllocator(
- CHAR *, LPUNKNOWN, HRESULT *,
- BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE);
-#endif
- virtual ~CBaseAllocator();
-
- DECLARE_IUNKNOWN
-
- // override this to publicise our interfaces
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- STDMETHODIMP SetProperties(
- ALLOCATOR_PROPERTIES* pRequest,
- ALLOCATOR_PROPERTIES* pActual);
-
- // return the properties actually being used on this allocator
- STDMETHODIMP GetProperties(
- ALLOCATOR_PROPERTIES* pProps);
-
- // override Commit to allocate memory. We handle the GetBuffer
- //state changes
- STDMETHODIMP Commit();
-
- // override this to handle the memory freeing. We handle any outstanding
- // GetBuffer calls
- STDMETHODIMP Decommit();
-
- // get container for a sample. Blocking, synchronous call to get the
- // next free buffer (as represented by an IMediaSample interface).
- // on return, the time etc properties will be invalid, but the buffer
- // pointer and size will be correct. The two time parameters are
- // optional and either may be NULL, they may alternatively be set to
- // the start and end times the sample will have attached to it
- // bPrevFramesSkipped is not used (used only by the video renderer's
- // allocator where it affects quality management in direct draw).
-
- STDMETHODIMP GetBuffer(IMediaSample **ppBuffer,
- REFERENCE_TIME * pStartTime,
- REFERENCE_TIME * pEndTime,
- DWORD dwFlags);
-
- // final release of a CMediaSample will call this
- STDMETHODIMP ReleaseBuffer(IMediaSample *pBuffer);
- // obsolete:: virtual void PutOnFreeList(CMediaSample * pSample);
-
- STDMETHODIMP SetNotify(IMemAllocatorNotifyCallbackTemp *pNotify);
-
- STDMETHODIMP GetFreeCount(LONG *plBuffersFree);
-
- // Notify that a sample is available
- void NotifySample();
-
- // Notify that we're waiting for a sample
- void SetWaiting() { m_lWaiting++; };
-};
-
-
-//=====================================================================
-//=====================================================================
-// Defines CMemAllocator
-//
-// this is an allocator based on CBaseAllocator that allocates sample
-// buffers in main memory (from 'new'). You must call SetProperties
-// before calling Commit.
-//
-// we don't free the memory when going into Decommit state. The simplest
-// way to implement this without complicating CBaseAllocator is to
-// have a Free() function, called to go into decommit state, that does
-// nothing and a ReallyFree function called from our destructor that
-// actually frees the memory.
-//=====================================================================
-//=====================================================================
-
-// Make me one from quartz.dll
-STDAPI CreateMemoryAllocator(IMemAllocator **ppAllocator);
-
-class CMemAllocator : public CBaseAllocator
-{
-
-protected:
-
- LPBYTE m_pBuffer; // combined memory for all buffers
-
- // override to free the memory when decommit completes
- // - we actually do nothing, and save the memory until deletion.
- void Free(void);
-
- // called from the destructor (and from Alloc if changing size/count) to
- // actually free up the memory
- void ReallyFree(void);
-
- // overriden to allocate the memory when commit called
- HRESULT Alloc(void);
-
-public:
- /* This goes in the factory template table to create new instances */
- static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *);
-
- STDMETHODIMP SetProperties(
- ALLOCATOR_PROPERTIES* pRequest,
- ALLOCATOR_PROPERTIES* pActual);
-
- CMemAllocator(TCHAR *, LPUNKNOWN, HRESULT *);
-#ifdef UNICODE
- CMemAllocator(CHAR *, LPUNKNOWN, HRESULT *);
-#endif
- ~CMemAllocator();
-};
-
-// helper used by IAMovieSetup implementation
-STDAPI
-AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata
- , IFilterMapper * pIFM
- , BOOL bRegister );
-
-
-///////////////////////////////////////////////////////////////////////////
-// ------------------------------------------------------------------------
-// ------------------------------------------------------------------------
-// ------------------------------------------------------------------------
-// ------------------------------------------------------------------------
-///////////////////////////////////////////////////////////////////////////
-
-#endif /* __FILTER__ */
-
-
-
+//------------------------------------------------------------------------------
+// File: AMFilter.h
+//
+// Desc: DirectShow base classes - efines class hierarchy for streams
+// architecture.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __FILTER__
+#define __FILTER__
+
+/* The following classes are declared in this header: */
+
+class CBaseMediaFilter; // IMediaFilter support
+class CBaseFilter; // IBaseFilter,IMediaFilter support
+class CBasePin; // Abstract base class for IPin interface
+class CEnumPins; // Enumerate input and output pins
+class CEnumMediaTypes; // Enumerate the pin's preferred formats
+class CBaseOutputPin; // Adds data provider member functions
+class CBaseInputPin; // Implements IMemInputPin interface
+class CMediaSample; // Basic transport unit for IMemInputPin
+class CBaseAllocator; // General list guff for most allocators
+class CMemAllocator; // Implements memory buffer allocation
+
+
+//=====================================================================
+//=====================================================================
+//
+// QueryFilterInfo and QueryPinInfo AddRef the interface pointers
+// they return. You can use the macro below to release the interface.
+//
+//=====================================================================
+//=====================================================================
+
+#define QueryFilterInfoReleaseGraph(fi) if ((fi).pGraph) (fi).pGraph->Release();
+
+#define QueryPinInfoReleaseFilter(pi) if ((pi).pFilter) (pi).pFilter->Release();
+
+//=====================================================================
+//=====================================================================
+// Defines CBaseMediaFilter
+//
+// Abstract base class implementing IMediaFilter.
+//
+// Typically you will derive your filter from CBaseFilter rather than
+// this, unless you are implementing an object such as a plug-in
+// distributor that needs to support IMediaFilter but not IBaseFilter.
+//
+// Note that IMediaFilter is derived from IPersist to allow query of
+// class id.
+//=====================================================================
+//=====================================================================
+
+class AM_NOVTABLE CBaseMediaFilter : public CUnknown,
+ public IMediaFilter
+{
+
+protected:
+
+ FILTER_STATE m_State; // current state: running, paused
+ IReferenceClock *m_pClock; // this filter's reference clock
+ // note: all filters in a filter graph use the same clock
+
+ // offset from stream time to reference time
+ CRefTime m_tStart;
+
+ CLSID m_clsid; // This filters clsid
+ // used for serialization
+ CCritSec *m_pLock; // Object we use for locking
+
+public:
+
+ CBaseMediaFilter(
+ __in_opt LPCTSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ __in CCritSec *pLock,
+ REFCLSID clsid);
+
+ virtual ~CBaseMediaFilter();
+
+ DECLARE_IUNKNOWN
+
+ // override this to say what interfaces we support where
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
+
+ //
+ // --- IPersist method ---
+ //
+
+ STDMETHODIMP GetClassID(__out CLSID *pClsID);
+
+ // --- IMediaFilter methods ---
+
+ STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State);
+
+ STDMETHODIMP SetSyncSource(__inout_opt IReferenceClock *pClock);
+
+ STDMETHODIMP GetSyncSource(__deref_out_opt IReferenceClock **pClock);
+
+ // default implementation of Stop and Pause just record the
+ // state. Override to activate or de-activate your filter.
+ // Note that Run when called from Stopped state will call Pause
+ // to ensure activation, so if you are a source or transform
+ // you will probably not need to override Run.
+ STDMETHODIMP Stop();
+ STDMETHODIMP Pause();
+
+
+ // the start parameter is the difference to be added to the
+ // sample's stream time to get the reference time for
+ // its presentation
+ STDMETHODIMP Run(REFERENCE_TIME tStart);
+
+ // --- helper methods ---
+
+ // return the current stream time - ie find out what
+ // stream time should be appearing now
+ virtual HRESULT StreamTime(CRefTime& rtStream);
+
+ // Is the filter currently active? (running or paused)
+ BOOL IsActive() {
+ CAutoLock cObjectLock(m_pLock);
+ return ((m_State == State_Paused) || (m_State == State_Running));
+ };
+};
+
+//=====================================================================
+//=====================================================================
+// Defines CBaseFilter
+//
+// An abstract class providing basic IBaseFilter support for pin
+// enumeration and filter information reading.
+//
+// We cannot derive from CBaseMediaFilter since methods in IMediaFilter
+// are also in IBaseFilter and would be ambiguous. Since much of the code
+// assumes that they derive from a class that has m_State and other state
+// directly available, we duplicate code from CBaseMediaFilter rather than
+// having a member variable.
+//
+// Derive your filter from this, or from a derived object such as
+// CTransformFilter.
+//=====================================================================
+//=====================================================================
+
+
+class AM_NOVTABLE CBaseFilter : public CUnknown, // Handles an IUnknown
+ public IBaseFilter, // The Filter Interface
+ public IAMovieSetup // For un/registration
+{
+
+friend class CBasePin;
+
+protected:
+ FILTER_STATE m_State; // current state: running, paused
+ IReferenceClock *m_pClock; // this graph's ref clock
+ CRefTime m_tStart; // offset from stream time to reference time
+ CLSID m_clsid; // This filters clsid
+ // used for serialization
+ CCritSec *m_pLock; // Object we use for locking
+
+ WCHAR *m_pName; // Full filter name
+ IFilterGraph *m_pGraph; // Graph we belong to
+ IMediaEventSink *m_pSink; // Called with notify events
+ LONG m_PinVersion; // Current pin version
+
+public:
+
+ CBaseFilter(
+ __in_opt LPCTSTR pName, // Object description
+ __inout_opt LPUNKNOWN pUnk, // IUnknown of delegating object
+ __in CCritSec *pLock, // Object who maintains lock
+ REFCLSID clsid); // The clsid to be used to serialize this filter
+
+ CBaseFilter(
+ __in_opt LPCTSTR pName, // Object description
+ __in_opt LPUNKNOWN pUnk, // IUnknown of delegating object
+ __in CCritSec *pLock, // Object who maintains lock
+ REFCLSID clsid, // The clsid to be used to serialize this filter
+ __inout HRESULT *phr); // General OLE return code
+#ifdef UNICODE
+ CBaseFilter(
+ __in_opt LPCSTR pName, // Object description
+ __in_opt LPUNKNOWN pUnk, // IUnknown of delegating object
+ __in CCritSec *pLock, // Object who maintains lock
+ REFCLSID clsid); // The clsid to be used to serialize this filter
+
+ CBaseFilter(
+ __in_opt LPCSTR pName, // Object description
+ __in_opt LPUNKNOWN pUnk, // IUnknown of delegating object
+ __in CCritSec *pLock, // Object who maintains lock
+ REFCLSID clsid, // The clsid to be used to serialize this filter
+ __inout HRESULT *phr); // General OLE return code
+#endif
+ ~CBaseFilter();
+
+ DECLARE_IUNKNOWN
+
+ // override this to say what interfaces we support where
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
+#ifdef DEBUG
+ STDMETHODIMP_(ULONG) NonDelegatingRelease();
+#endif
+
+ //
+ // --- IPersist method ---
+ //
+
+ STDMETHODIMP GetClassID(__out CLSID *pClsID);
+
+ // --- IMediaFilter methods ---
+
+ STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State);
+
+ STDMETHODIMP SetSyncSource(__in_opt IReferenceClock *pClock);
+
+ STDMETHODIMP GetSyncSource(__deref_out_opt IReferenceClock **pClock);
+
+
+ // override Stop and Pause so we can activate the pins.
+ // Note that Run will call Pause first if activation needed.
+ // Override these if you want to activate your filter rather than
+ // your pins.
+ STDMETHODIMP Stop();
+ STDMETHODIMP Pause();
+
+ // the start parameter is the difference to be added to the
+ // sample's stream time to get the reference time for
+ // its presentation
+ STDMETHODIMP Run(REFERENCE_TIME tStart);
+
+ // --- helper methods ---
+
+ // return the current stream time - ie find out what
+ // stream time should be appearing now
+ virtual HRESULT StreamTime(CRefTime& rtStream);
+
+ // Is the filter currently active?
+ BOOL IsActive() {
+ CAutoLock cObjectLock(m_pLock);
+ return ((m_State == State_Paused) || (m_State == State_Running));
+ };
+
+ // Is this filter stopped (without locking)
+ BOOL IsStopped() {
+ return (m_State == State_Stopped);
+ };
+
+ //
+ // --- IBaseFilter methods ---
+ //
+
+ // pin enumerator
+ STDMETHODIMP EnumPins(
+ __deref_out IEnumPins ** ppEnum);
+
+
+ // default behaviour of FindPin assumes pin ids are their names
+ STDMETHODIMP FindPin(
+ LPCWSTR Id,
+ __deref_out IPin ** ppPin
+ );
+
+ STDMETHODIMP QueryFilterInfo(
+ __out FILTER_INFO * pInfo);
+
+ STDMETHODIMP JoinFilterGraph(
+ __inout_opt IFilterGraph * pGraph,
+ __in_opt LPCWSTR pName);
+
+ // return a Vendor information string. Optional - may return E_NOTIMPL.
+ // memory returned should be freed using CoTaskMemFree
+ // default implementation returns E_NOTIMPL
+ STDMETHODIMP QueryVendorInfo(
+ __deref_out LPWSTR* pVendorInfo
+ );
+
+ // --- helper methods ---
+
+ // send an event notification to the filter graph if we know about it.
+ // returns S_OK if delivered, S_FALSE if the filter graph does not sink
+ // events, or an error otherwise.
+ HRESULT NotifyEvent(
+ long EventCode,
+ LONG_PTR EventParam1,
+ LONG_PTR EventParam2);
+
+ // return the filter graph we belong to
+ __out_opt IFilterGraph *GetFilterGraph() {
+ return m_pGraph;
+ }
+
+ // Request reconnect
+ // pPin is the pin to reconnect
+ // pmt is the type to reconnect with - can be NULL
+ // Calls ReconnectEx on the filter graph
+ HRESULT ReconnectPin(IPin *pPin, __in_opt AM_MEDIA_TYPE const *pmt);
+
+ // find out the current pin version (used by enumerators)
+ virtual LONG GetPinVersion();
+ void IncrementPinVersion();
+
+ // you need to supply these to access the pins from the enumerator
+ // and for default Stop and Pause/Run activation.
+ virtual int GetPinCount() PURE;
+ virtual CBasePin *GetPin(int n) PURE;
+
+ // --- IAMovieSetup methods ---
+
+ STDMETHODIMP Register(); // ask filter to register itself
+ STDMETHODIMP Unregister(); // and unregister itself
+
+ // --- setup helper methods ---
+ // (override to return filters setup data)
+
+ virtual __out_opt LPAMOVIESETUP_FILTER GetSetupData(){ return NULL; }
+
+};
+
+
+//=====================================================================
+//=====================================================================
+// Defines CBasePin
+//
+// Abstract class that supports the basics of IPin
+//=====================================================================
+//=====================================================================
+
+class AM_NOVTABLE CBasePin : public CUnknown, public IPin, public IQualityControl
+{
+
+protected:
+
+ WCHAR * m_pName; // This pin's name
+ IPin *m_Connected; // Pin we have connected to
+ PIN_DIRECTION m_dir; // Direction of this pin
+ CCritSec *m_pLock; // Object we use for locking
+ bool m_bRunTimeError; // Run time error generated
+ bool m_bCanReconnectWhenActive; // OK to reconnect when active
+ bool m_bTryMyTypesFirst; // When connecting enumerate
+ // this pin's types first
+ CBaseFilter *m_pFilter; // Filter we were created by
+ IQualityControl *m_pQSink; // Target for Quality messages
+ LONG m_TypeVersion; // Holds current type version
+ CMediaType m_mt; // Media type of connection
+
+ CRefTime m_tStart; // time from NewSegment call
+ CRefTime m_tStop; // time from NewSegment
+ double m_dRate; // rate from NewSegment
+
+#ifdef DEBUG
+ LONG m_cRef; // Ref count tracing
+#endif
+
+ // displays pin connection information
+
+#ifdef DEBUG
+ void DisplayPinInfo(IPin *pReceivePin);
+ void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt);
+#else
+ void DisplayPinInfo(IPin *pReceivePin) {};
+ void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt) {};
+#endif
+
+ // used to agree a media type for a pin connection
+
+ // given a specific media type, attempt a connection (includes
+ // checking that the type is acceptable to this pin)
+ HRESULT
+ AttemptConnection(
+ IPin* pReceivePin, // connect to this pin
+ const CMediaType* pmt // using this type
+ );
+
+ // try all the media types in this enumerator - for each that
+ // we accept, try to connect using ReceiveConnection.
+ HRESULT TryMediaTypes(
+ IPin *pReceivePin, // connect to this pin
+ __in_opt const CMediaType *pmt, // proposed type from Connect
+ IEnumMediaTypes *pEnum); // try this enumerator
+
+ // establish a connection with a suitable mediatype. Needs to
+ // propose a media type if the pmt pointer is null or partially
+ // specified - use TryMediaTypes on both our and then the other pin's
+ // enumerator until we find one that works.
+ HRESULT AgreeMediaType(
+ IPin *pReceivePin, // connect to this pin
+ const CMediaType *pmt); // proposed type from Connect
+
+public:
+
+ CBasePin(
+ __in_opt LPCTSTR pObjectName, // Object description
+ __in CBaseFilter *pFilter, // Owning filter who knows about pins
+ __in CCritSec *pLock, // Object who implements the lock
+ __inout HRESULT *phr, // General OLE return code
+ __in_opt LPCWSTR pName, // Pin name for us
+ PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT
+#ifdef UNICODE
+ CBasePin(
+ __in_opt LPCSTR pObjectName, // Object description
+ __in CBaseFilter *pFilter, // Owning filter who knows about pins
+ __in CCritSec *pLock, // Object who implements the lock
+ __inout HRESULT *phr, // General OLE return code
+ __in_opt LPCWSTR pName, // Pin name for us
+ PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT
+#endif
+ virtual ~CBasePin();
+
+ DECLARE_IUNKNOWN
+
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
+ STDMETHODIMP_(ULONG) NonDelegatingRelease();
+ STDMETHODIMP_(ULONG) NonDelegatingAddRef();
+
+ // --- IPin methods ---
+
+ // take lead role in establishing a connection. Media type pointer
+ // may be null, or may point to partially-specified mediatype
+ // (subtype or format type may be GUID_NULL).
+ STDMETHODIMP Connect(
+ IPin * pReceivePin,
+ __in_opt const AM_MEDIA_TYPE *pmt // optional media type
+ );
+
+ // (passive) accept a connection from another pin
+ STDMETHODIMP ReceiveConnection(
+ IPin * pConnector, // this is the initiating connecting pin
+ const AM_MEDIA_TYPE *pmt // this is the media type we will exchange
+ );
+
+ STDMETHODIMP Disconnect();
+
+ STDMETHODIMP ConnectedTo(__deref_out IPin **pPin);
+
+ STDMETHODIMP ConnectionMediaType(__out AM_MEDIA_TYPE *pmt);
+
+ STDMETHODIMP QueryPinInfo(
+ __out PIN_INFO * pInfo
+ );
+
+ STDMETHODIMP QueryDirection(
+ __out PIN_DIRECTION * pPinDir
+ );
+
+ STDMETHODIMP QueryId(
+ __deref_out LPWSTR * Id
+ );
+
+ // does the pin support this media type
+ STDMETHODIMP QueryAccept(
+ const AM_MEDIA_TYPE *pmt
+ );
+
+ // return an enumerator for this pins preferred media types
+ STDMETHODIMP EnumMediaTypes(
+ __deref_out IEnumMediaTypes **ppEnum
+ );
+
+ // return an array of IPin* - the pins that this pin internally connects to
+ // All pins put in the array must be AddReffed (but no others)
+ // Errors: "Can't say" - FAIL, not enough slots - return S_FALSE
+ // Default: return E_NOTIMPL
+ // The filter graph will interpret NOT_IMPL as any input pin connects to
+ // all visible output pins and vice versa.
+ // apPin can be NULL if nPin==0 (not otherwise).
+ STDMETHODIMP QueryInternalConnections(
+ __out_ecount_part(*nPin,*nPin) IPin* *apPin, // array of IPin*
+ __inout ULONG *nPin // on input, the number of slots
+ // on output the number of pins
+ ) { return E_NOTIMPL; }
+
+ // Called when no more data will be sent
+ STDMETHODIMP EndOfStream(void);
+
+ // Begin/EndFlush still PURE
+
+ // NewSegment notifies of the start/stop/rate applying to the data
+ // about to be received. Default implementation records data and
+ // returns S_OK.
+ // Override this to pass downstream.
+ STDMETHODIMP NewSegment(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop,
+ double dRate);
+
+ //================================================================================
+ // IQualityControl methods
+ //================================================================================
+
+ STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
+
+ STDMETHODIMP SetSink(IQualityControl * piqc);
+
+ // --- helper methods ---
+
+ // Returns true if the pin is connected. false otherwise.
+ BOOL IsConnected(void) {return (m_Connected != NULL); };
+ // Return the pin this is connected to (if any)
+ IPin * GetConnected() { return m_Connected; };
+
+ // Check if our filter is currently stopped
+ BOOL IsStopped() {
+ return (m_pFilter->m_State == State_Stopped);
+ };
+
+ // find out the current type version (used by enumerators)
+ virtual LONG GetMediaTypeVersion();
+ void IncrementTypeVersion();
+
+ // switch the pin to active (paused or running) mode
+ // not an error to call this if already active
+ virtual HRESULT Active(void);
+
+ // switch the pin to inactive state - may already be inactive
+ virtual HRESULT Inactive(void);
+
+ // Notify of Run() from filter
+ virtual HRESULT Run(REFERENCE_TIME tStart);
+
+ // check if the pin can support this specific proposed type and format
+ virtual HRESULT CheckMediaType(const CMediaType *) PURE;
+
+ // set the connection to use this format (previously agreed)
+ virtual HRESULT SetMediaType(const CMediaType *);
+
+ // check that the connection is ok before verifying it
+ // can be overridden eg to check what interfaces will be supported.
+ virtual HRESULT CheckConnect(IPin *);
+
+ // Set and release resources required for a connection
+ virtual HRESULT BreakConnect();
+ virtual HRESULT CompleteConnect(IPin *pReceivePin);
+
+ // returns the preferred formats for a pin
+ virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType);
+
+ // access to NewSegment values
+ REFERENCE_TIME CurrentStopTime() {
+ return m_tStop;
+ }
+ REFERENCE_TIME CurrentStartTime() {
+ return m_tStart;
+ }
+ double CurrentRate() {
+ return m_dRate;
+ }
+
+ // Access name
+ LPWSTR Name() { return m_pName; };
+
+ // Can reconnectwhen active?
+ void SetReconnectWhenActive(bool bCanReconnect)
+ {
+ m_bCanReconnectWhenActive = bCanReconnect;
+ }
+
+ bool CanReconnectWhenActive()
+ {
+ return m_bCanReconnectWhenActive;
+ }
+
+protected:
+ STDMETHODIMP DisconnectInternal();
+};
+
+
+//=====================================================================
+//=====================================================================
+// Defines CEnumPins
+//
+// Pin enumerator class that works by calling CBaseFilter. This interface
+// is provided by CBaseFilter::EnumPins and calls GetPinCount() and
+// GetPin() to enumerate existing pins. Needs to be a separate object so
+// that it can be cloned (creating an existing object at the same
+// position in the enumeration)
+//
+//=====================================================================
+//=====================================================================
+
+class CEnumPins : public IEnumPins // The interface we support
+{
+ int m_Position; // Current ordinal position
+ int m_PinCount; // Number of pins available
+ CBaseFilter *m_pFilter; // The filter who owns us
+ LONG m_Version; // Pin version information
+ LONG m_cRef;
+
+ typedef CGenericList CPinList;
+
+ CPinList m_PinCache; // These pointers have not been AddRef'ed and
+ // so they should not be dereferenced. They are
+ // merely kept to ID which pins have been enumerated.
+
+#ifdef DEBUG
+ DWORD m_dwCookie;
+#endif
+
+ /* If while we are retrieving a pin for example from the filter an error
+ occurs we assume that our internal state is stale with respect to the
+ filter (someone may have deleted all the pins). We can check before
+ starting whether or not the operation is likely to fail by asking the
+ filter what it's current version number is. If the filter has not
+ overriden the GetPinVersion method then this will always match */
+
+ BOOL AreWeOutOfSync() {
+ return (m_pFilter->GetPinVersion() == m_Version ? FALSE : TRUE);
+ };
+
+ /* This method performs the same operations as Reset, except is does not clear
+ the cache of pins already enumerated. */
+
+ STDMETHODIMP Refresh();
+
+public:
+
+ CEnumPins(
+ __in CBaseFilter *pFilter,
+ __in_opt CEnumPins *pEnumPins);
+
+ virtual ~CEnumPins();
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IEnumPins
+ STDMETHODIMP Next(
+ ULONG cPins, // place this many pins...
+ __out_ecount(cPins) IPin ** ppPins, // ...in this array of IPin*
+ __out_opt ULONG * pcFetched // actual count passed returned here
+ );
+
+ STDMETHODIMP Skip(ULONG cPins);
+ STDMETHODIMP Reset();
+ STDMETHODIMP Clone(__deref_out IEnumPins **ppEnum);
+
+
+};
+
+
+//=====================================================================
+//=====================================================================
+// Defines CEnumMediaTypes
+//
+// Enumerates the preferred formats for input and output pins
+//=====================================================================
+//=====================================================================
+
+class CEnumMediaTypes : public IEnumMediaTypes // The interface we support
+{
+ int m_Position; // Current ordinal position
+ CBasePin *m_pPin; // The pin who owns us
+ LONG m_Version; // Media type version value
+ LONG m_cRef;
+#ifdef DEBUG
+ DWORD m_dwCookie;
+#endif
+
+ /* The media types a filter supports can be quite dynamic so we add to
+ the general IEnumXXXX interface the ability to be signaled when they
+ change via an event handle the connected filter supplies. Until the
+ Reset method is called after the state changes all further calls to
+ the enumerator (except Reset) will return E_UNEXPECTED error code */
+
+ BOOL AreWeOutOfSync() {
+ return (m_pPin->GetMediaTypeVersion() == m_Version ? FALSE : TRUE);
+ };
+
+public:
+
+ CEnumMediaTypes(
+ __in CBasePin *pPin,
+ __in_opt CEnumMediaTypes *pEnumMediaTypes);
+
+ virtual ~CEnumMediaTypes();
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IEnumMediaTypes
+ STDMETHODIMP Next(
+ ULONG cMediaTypes, // place this many pins...
+ __out_ecount(cMediaTypes) AM_MEDIA_TYPE ** ppMediaTypes, // ...in this array
+ __out_opt ULONG * pcFetched // actual count passed
+ );
+
+ STDMETHODIMP Skip(ULONG cMediaTypes);
+ STDMETHODIMP Reset();
+ STDMETHODIMP Clone(__deref_out IEnumMediaTypes **ppEnum);
+};
+
+
+
+
+//=====================================================================
+//=====================================================================
+// Defines CBaseOutputPin
+//
+// class derived from CBasePin that can pass buffers to a connected pin
+// that supports IMemInputPin. Supports IPin.
+//
+// Derive your output pin from this.
+//
+//=====================================================================
+//=====================================================================
+
+class AM_NOVTABLE CBaseOutputPin : public CBasePin
+{
+
+protected:
+
+ IMemAllocator *m_pAllocator;
+ IMemInputPin *m_pInputPin; // interface on the downstreaminput pin
+ // set up in CheckConnect when we connect.
+
+public:
+
+ CBaseOutputPin(
+ __in_opt LPCTSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName);
+#ifdef UNICODE
+ CBaseOutputPin(
+ __in_opt LPCSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName);
+#endif
+ // override CompleteConnect() so we can negotiate an allocator
+ virtual HRESULT CompleteConnect(IPin *pReceivePin);
+
+ // negotiate the allocator and its buffer size/count and other properties
+ // Calls DecideBufferSize to set properties
+ virtual HRESULT DecideAllocator(IMemInputPin * pPin, __deref_out IMemAllocator ** pAlloc);
+
+ // override this to set the buffer size and count. Return an error
+ // if the size/count is not to your liking.
+ // The allocator properties passed in are those requested by the
+ // input pin - use eg the alignment and prefix members if you have
+ // no preference on these.
+ virtual HRESULT DecideBufferSize(
+ IMemAllocator * pAlloc,
+ __inout ALLOCATOR_PROPERTIES * ppropInputRequest
+ ) PURE;
+
+ // returns an empty sample buffer from the allocator
+ virtual HRESULT GetDeliveryBuffer(__deref_out IMediaSample ** ppSample,
+ __in_opt REFERENCE_TIME * pStartTime,
+ __in_opt REFERENCE_TIME * pEndTime,
+ DWORD dwFlags);
+
+ // deliver a filled-in sample to the connected input pin
+ // note - you need to release it after calling this. The receiving
+ // pin will addref the sample if it needs to hold it beyond the
+ // call.
+ virtual HRESULT Deliver(IMediaSample *);
+
+ // override this to control the connection
+ virtual HRESULT InitAllocator(__deref_out IMemAllocator **ppAlloc);
+ HRESULT CheckConnect(IPin *pPin);
+ HRESULT BreakConnect();
+
+ // override to call Commit and Decommit
+ HRESULT Active(void);
+ HRESULT Inactive(void);
+
+ // we have a default handling of EndOfStream which is to return
+ // an error, since this should be called on input pins only
+ STDMETHODIMP EndOfStream(void);
+
+ // called from elsewhere in our filter to pass EOS downstream to
+ // our connected input pin
+ virtual HRESULT DeliverEndOfStream(void);
+
+ // same for Begin/EndFlush - we handle Begin/EndFlush since it
+ // is an error on an output pin, and we have Deliver methods to
+ // call the methods on the connected pin
+ STDMETHODIMP BeginFlush(void);
+ STDMETHODIMP EndFlush(void);
+ virtual HRESULT DeliverBeginFlush(void);
+ virtual HRESULT DeliverEndFlush(void);
+
+ // deliver NewSegment to connected pin - you will need to
+ // override this if you queue any data in your output pin.
+ virtual HRESULT DeliverNewSegment(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop,
+ double dRate);
+
+ //================================================================================
+ // IQualityControl methods
+ //================================================================================
+
+ // All inherited from CBasePin and not overridden here.
+ // STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
+ // STDMETHODIMP SetSink(IQualityControl * piqc);
+};
+
+
+//=====================================================================
+//=====================================================================
+// Defines CBaseInputPin
+//
+// derive your standard input pin from this.
+// you need to supply GetMediaType and CheckConnect etc (see CBasePin),
+// and you need to supply Receive to do something more useful.
+//
+//=====================================================================
+//=====================================================================
+
+class AM_NOVTABLE CBaseInputPin : public CBasePin,
+ public IMemInputPin
+{
+
+protected:
+
+ IMemAllocator *m_pAllocator; // Default memory allocator
+
+ // allocator is read-only, so received samples
+ // cannot be modified (probably only relevant to in-place
+ // transforms
+ BYTE m_bReadOnly;
+
+ // in flushing state (between BeginFlush and EndFlush)
+ // if TRUE, all Receives are returned with S_FALSE
+ BYTE m_bFlushing;
+
+ // Sample properties - initalized in Receive
+ AM_SAMPLE2_PROPERTIES m_SampleProps;
+
+public:
+
+ CBaseInputPin(
+ __in_opt LPCTSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName);
+#ifdef UNICODE
+ CBaseInputPin(
+ __in_opt LPCSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName);
+#endif
+ virtual ~CBaseInputPin();
+
+ DECLARE_IUNKNOWN
+
+ // override this to publicise our interfaces
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+ // return the allocator interface that this input pin
+ // would like the output pin to use
+ STDMETHODIMP GetAllocator(__deref_out IMemAllocator ** ppAllocator);
+
+ // tell the input pin which allocator the output pin is actually
+ // going to use.
+ STDMETHODIMP NotifyAllocator(
+ IMemAllocator * pAllocator,
+ BOOL bReadOnly);
+
+ // do something with this media sample
+ STDMETHODIMP Receive(IMediaSample *pSample);
+
+ // do something with these media samples
+ STDMETHODIMP ReceiveMultiple (
+ __in_ecount(nSamples) IMediaSample **pSamples,
+ long nSamples,
+ __out long *nSamplesProcessed);
+
+ // See if Receive() blocks
+ STDMETHODIMP ReceiveCanBlock();
+
+ // Default handling for BeginFlush - call at the beginning
+ // of your implementation (makes sure that all Receive calls
+ // fail). After calling this, you need to free any queued data
+ // and then call downstream.
+ STDMETHODIMP BeginFlush(void);
+
+ // default handling for EndFlush - call at end of your implementation
+ // - before calling this, ensure that there is no queued data and no thread
+ // pushing any more without a further receive, then call downstream,
+ // then call this method to clear the m_bFlushing flag and re-enable
+ // receives
+ STDMETHODIMP EndFlush(void);
+
+ // this method is optional (can return E_NOTIMPL).
+ // default implementation returns E_NOTIMPL. Override if you have
+ // specific alignment or prefix needs, but could use an upstream
+ // allocator
+ STDMETHODIMP GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps);
+
+ // Release the pin's allocator.
+ HRESULT BreakConnect();
+
+ // helper method to check the read-only flag
+ BOOL IsReadOnly() {
+ return m_bReadOnly;
+ };
+
+ // helper method to see if we are flushing
+ BOOL IsFlushing() {
+ return m_bFlushing;
+ };
+
+ // Override this for checking whether it's OK to process samples
+ // Also call this from EndOfStream.
+ virtual HRESULT CheckStreaming();
+
+ // Pass a Quality notification on to the appropriate sink
+ HRESULT PassNotify(Quality& q);
+
+
+ //================================================================================
+ // IQualityControl methods (from CBasePin)
+ //================================================================================
+
+ STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
+
+ // no need to override:
+ // STDMETHODIMP SetSink(IQualityControl * piqc);
+
+
+ // switch the pin to inactive state - may already be inactive
+ virtual HRESULT Inactive(void);
+
+ // Return sample properties pointer
+ AM_SAMPLE2_PROPERTIES * SampleProps() {
+ ASSERT(m_SampleProps.cbData != 0);
+ return &m_SampleProps;
+ }
+
+};
+
+///////////////////////////////////////////////////////////////////////////
+// CDynamicOutputPin
+//
+
+class CDynamicOutputPin : public CBaseOutputPin,
+ public IPinFlowControl
+{
+public:
+#ifdef UNICODE
+ CDynamicOutputPin(
+ __in_opt LPCSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName);
+#endif
+
+ CDynamicOutputPin(
+ __in_opt LPCTSTR pObjectName,
+ __in CBaseFilter *pFilter,
+ __in CCritSec *pLock,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pName);
+
+ ~CDynamicOutputPin();
+
+ // IUnknown Methods
+ DECLARE_IUNKNOWN
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+ // IPin Methods
+ STDMETHODIMP Disconnect(void);
+
+ // IPinFlowControl Methods
+ STDMETHODIMP Block(DWORD dwBlockFlags, HANDLE hEvent);
+
+ // Set graph config info
+ void SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent);
+
+ #ifdef DEBUG
+ virtual HRESULT Deliver(IMediaSample *pSample);
+ virtual HRESULT DeliverEndOfStream(void);
+ virtual HRESULT DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate);
+ #endif // DEBUG
+
+ HRESULT DeliverBeginFlush(void);
+ HRESULT DeliverEndFlush(void);
+
+ HRESULT Inactive(void);
+ HRESULT Active(void);
+ virtual HRESULT CompleteConnect(IPin *pReceivePin);
+
+ virtual HRESULT StartUsingOutputPin(void);
+ virtual void StopUsingOutputPin(void);
+ virtual bool StreamingThreadUsingOutputPin(void);
+
+ HRESULT ChangeOutputFormat
+ (
+ const AM_MEDIA_TYPE *pmt,
+ REFERENCE_TIME tSegmentStart,
+ REFERENCE_TIME tSegmentStop,
+ double dSegmentRate
+ );
+ HRESULT ChangeMediaType(const CMediaType *pmt);
+ HRESULT DynamicReconnect(const CMediaType *pmt);
+
+protected:
+ HRESULT SynchronousBlockOutputPin(void);
+ HRESULT AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent);
+ HRESULT UnblockOutputPin(void);
+
+ void BlockOutputPin(void);
+ void ResetBlockState(void);
+
+ static HRESULT WaitEvent(HANDLE hEvent);
+
+ enum BLOCK_STATE
+ {
+ NOT_BLOCKED,
+ PENDING,
+ BLOCKED
+ };
+
+ // This lock should be held when the following class members are
+ // being used: m_hNotifyCallerPinBlockedEvent, m_BlockState,
+ // m_dwBlockCallerThreadID and m_dwNumOutstandingOutputPinUsers.
+ CCritSec m_BlockStateLock;
+
+ // This event should be signaled when the output pin is
+ // not blocked. This is a manual reset event. For more
+ // information on events, see the documentation for
+ // CreateEvent() in the Windows SDK.
+ HANDLE m_hUnblockOutputPinEvent;
+
+ // This event will be signaled when block operation succeedes or
+ // when the user cancels the block operation. The block operation
+ // can be canceled by calling IPinFlowControl2::Block( 0, NULL )
+ // while the block operation is pending.
+ HANDLE m_hNotifyCallerPinBlockedEvent;
+
+ // The state of the current block operation.
+ BLOCK_STATE m_BlockState;
+
+ // The ID of the thread which last called IPinFlowControl::Block().
+ // For more information on thread IDs, see the documentation for
+ // GetCurrentThreadID() in the Windows SDK.
+ DWORD m_dwBlockCallerThreadID;
+
+ // The number of times StartUsingOutputPin() has been sucessfully
+ // called and a corresponding call to StopUsingOutputPin() has not
+ // been made. When this variable is greater than 0, the streaming
+ // thread is calling IPin::NewSegment(), IPin::EndOfStream(),
+ // IMemInputPin::Receive() or IMemInputPin::ReceiveMultiple(). The
+ // streaming thread could also be calling: DynamicReconnect(),
+ // ChangeMediaType() or ChangeOutputFormat(). The output pin cannot
+ // be blocked while the output pin is being used.
+ DWORD m_dwNumOutstandingOutputPinUsers;
+
+ // This event should be set when the IMediaFilter::Stop() is called.
+ // This is a manual reset event. It is also set when the output pin
+ // delivers a flush to the connected input pin.
+ HANDLE m_hStopEvent;
+ IGraphConfig* m_pGraphConfig;
+
+ // TRUE if the output pin's allocator's samples are read only.
+ // Otherwise FALSE. For more information, see the documentation
+ // for IMemInputPin::NotifyAllocator().
+ BOOL m_bPinUsesReadOnlyAllocator;
+
+private:
+ HRESULT Initialize(void);
+ HRESULT ChangeMediaTypeHelper(const CMediaType *pmt);
+
+ #ifdef DEBUG
+ void AssertValid(void);
+ #endif // DEBUG
+};
+
+class CAutoUsingOutputPin
+{
+public:
+ CAutoUsingOutputPin( __in CDynamicOutputPin* pOutputPin, __inout HRESULT* phr );
+ ~CAutoUsingOutputPin();
+
+private:
+ CDynamicOutputPin* m_pOutputPin;
+};
+
+inline CAutoUsingOutputPin::CAutoUsingOutputPin( __in CDynamicOutputPin* pOutputPin, __inout HRESULT* phr ) :
+ m_pOutputPin(NULL)
+{
+ // The caller should always pass in valid pointers.
+ ASSERT( NULL != pOutputPin );
+ ASSERT( NULL != phr );
+
+ // Make sure the user initialized phr.
+ ASSERT( S_OK == *phr );
+
+ HRESULT hr = pOutputPin->StartUsingOutputPin();
+ if( FAILED( hr ) )
+ {
+ *phr = hr;
+ return;
+ }
+
+ m_pOutputPin = pOutputPin;
+}
+
+inline CAutoUsingOutputPin::~CAutoUsingOutputPin()
+{
+ if( NULL != m_pOutputPin )
+ {
+ m_pOutputPin->StopUsingOutputPin();
+ }
+}
+
+#ifdef DEBUG
+
+inline HRESULT CDynamicOutputPin::Deliver(IMediaSample *pSample)
+{
+ // The caller should call StartUsingOutputPin() before calling this
+ // method.
+ ASSERT(StreamingThreadUsingOutputPin());
+
+ return CBaseOutputPin::Deliver(pSample);
+}
+
+inline HRESULT CDynamicOutputPin::DeliverEndOfStream(void)
+{
+ // The caller should call StartUsingOutputPin() before calling this
+ // method.
+ ASSERT( StreamingThreadUsingOutputPin() );
+
+ return CBaseOutputPin::DeliverEndOfStream();
+}
+
+inline HRESULT CDynamicOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
+{
+ // The caller should call StartUsingOutputPin() before calling this
+ // method.
+ ASSERT(StreamingThreadUsingOutputPin());
+
+ return CBaseOutputPin::DeliverNewSegment(tStart, tStop, dRate);
+}
+
+#endif // DEBUG
+
+//=====================================================================
+//=====================================================================
+// Memory allocators
+//
+// the shared memory transport between pins requires the input pin
+// to provide a memory allocator that can provide sample objects. A
+// sample object supports the IMediaSample interface.
+//
+// CBaseAllocator handles the management of free and busy samples. It
+// allocates CMediaSample objects. CBaseAllocator is an abstract class:
+// in particular it has no method of initializing the list of free
+// samples. CMemAllocator is derived from CBaseAllocator and initializes
+// the list of samples using memory from the standard IMalloc interface.
+//
+// If you want your buffers to live in some special area of memory,
+// derive your allocator object from CBaseAllocator. If you derive your
+// IMemInputPin interface object from CBaseMemInputPin, you will get
+// CMemAllocator-based allocation etc for free and will just need to
+// supply the Receive handling, and media type / format negotiation.
+//=====================================================================
+//=====================================================================
+
+
+//=====================================================================
+//=====================================================================
+// Defines CMediaSample
+//
+// an object of this class supports IMediaSample and represents a buffer
+// for media data with some associated properties. Releasing it returns
+// it to a freelist managed by a CBaseAllocator derived object.
+//=====================================================================
+//=====================================================================
+
+class CMediaSample : public IMediaSample2 // The interface we support
+{
+
+protected:
+
+ friend class CBaseAllocator;
+
+ /* Values for dwFlags - these are used for backward compatiblity
+ only now - use AM_SAMPLE_xxx
+ */
+ enum { Sample_SyncPoint = 0x01, /* Is this a sync point */
+ Sample_Preroll = 0x02, /* Is this a preroll sample */
+ Sample_Discontinuity = 0x04, /* Set if start of new segment */
+ Sample_TypeChanged = 0x08, /* Has the type changed */
+ Sample_TimeValid = 0x10, /* Set if time is valid */
+ Sample_MediaTimeValid = 0x20, /* Is the media time valid */
+ Sample_TimeDiscontinuity = 0x40, /* Time discontinuity */
+ Sample_StopValid = 0x100, /* Stop time valid */
+ Sample_ValidFlags = 0x1FF
+ };
+
+ /* Properties, the media sample class can be a container for a format
+ change in which case we take a copy of a type through the SetMediaType
+ interface function and then return it when GetMediaType is called. As
+ we do no internal processing on it we leave it as a pointer */
+
+ DWORD m_dwFlags; /* Flags for this sample */
+ /* Type specific flags are packed
+ into the top word
+ */
+ DWORD m_dwTypeSpecificFlags; /* Media type specific flags */
+ __field_ecount_opt(m_cbBuffer) LPBYTE m_pBuffer; /* Pointer to the complete buffer */
+ LONG m_lActual; /* Length of data in this sample */
+ LONG m_cbBuffer; /* Size of the buffer */
+ CBaseAllocator *m_pAllocator; /* The allocator who owns us */
+ CMediaSample *m_pNext; /* Chaining in free list */
+ REFERENCE_TIME m_Start; /* Start sample time */
+ REFERENCE_TIME m_End; /* End sample time */
+ LONGLONG m_MediaStart; /* Real media start position */
+ LONG m_MediaEnd; /* A difference to get the end */
+ AM_MEDIA_TYPE *m_pMediaType; /* Media type change data */
+ DWORD m_dwStreamId; /* Stream id */
+public:
+ LONG m_cRef; /* Reference count */
+
+
+public:
+
+ CMediaSample(
+ __in_opt LPCTSTR pName,
+ __in_opt CBaseAllocator *pAllocator,
+ __inout_opt HRESULT *phr,
+ __in_bcount_opt(length) LPBYTE pBuffer = NULL,
+ LONG length = 0);
+#ifdef UNICODE
+ CMediaSample(
+ __in_opt LPCSTR pName,
+ __in_opt CBaseAllocator *pAllocator,
+ __inout_opt HRESULT *phr,
+ __in_bcount_opt(length) LPBYTE pBuffer = NULL,
+ LONG length = 0);
+#endif
+
+ virtual ~CMediaSample();
+
+ /* Note the media sample does not delegate to its owner */
+
+ STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // set the buffer pointer and length. Used by allocators that
+ // want variable sized pointers or pointers into already-read data.
+ // This is only available through a CMediaSample* not an IMediaSample*
+ // and so cannot be changed by clients.
+ HRESULT SetPointer(__in_bcount(cBytes) BYTE * ptr, LONG cBytes);
+
+ // Get me a read/write pointer to this buffer's memory.
+ STDMETHODIMP GetPointer(__deref_out BYTE ** ppBuffer);
+
+ STDMETHODIMP_(LONG) GetSize(void);
+
+ // get the stream time at which this sample should start and finish.
+ STDMETHODIMP GetTime(
+ __out REFERENCE_TIME * pTimeStart, // put time here
+ __out REFERENCE_TIME * pTimeEnd
+ );
+
+ // Set the stream time at which this sample should start and finish.
+ STDMETHODIMP SetTime(
+ __in_opt REFERENCE_TIME * pTimeStart, // put time here
+ __in_opt REFERENCE_TIME * pTimeEnd
+ );
+ STDMETHODIMP IsSyncPoint(void);
+ STDMETHODIMP SetSyncPoint(BOOL bIsSyncPoint);
+ STDMETHODIMP IsPreroll(void);
+ STDMETHODIMP SetPreroll(BOOL bIsPreroll);
+
+ STDMETHODIMP_(LONG) GetActualDataLength(void);
+ STDMETHODIMP SetActualDataLength(LONG lActual);
+
+ // these allow for limited format changes in band
+
+ STDMETHODIMP GetMediaType(__deref_out AM_MEDIA_TYPE **ppMediaType);
+ STDMETHODIMP SetMediaType(__in_opt AM_MEDIA_TYPE *pMediaType);
+
+ // returns S_OK if there is a discontinuity in the data (this same is
+ // not a continuation of the previous stream of data
+ // - there has been a seek).
+ STDMETHODIMP IsDiscontinuity(void);
+ // set the discontinuity property - TRUE if this sample is not a
+ // continuation, but a new sample after a seek.
+ STDMETHODIMP SetDiscontinuity(BOOL bDiscontinuity);
+
+ // get the media times for this sample
+ STDMETHODIMP GetMediaTime(
+ __out LONGLONG * pTimeStart,
+ __out LONGLONG * pTimeEnd
+ );
+
+ // Set the media times for this sample
+ STDMETHODIMP SetMediaTime(
+ __in_opt LONGLONG * pTimeStart,
+ __in_opt LONGLONG * pTimeEnd
+ );
+
+ // Set and get properties (IMediaSample2)
+ STDMETHODIMP GetProperties(
+ DWORD cbProperties,
+ __out_bcount(cbProperties) BYTE * pbProperties
+ );
+
+ STDMETHODIMP SetProperties(
+ DWORD cbProperties,
+ __in_bcount(cbProperties) const BYTE * pbProperties
+ );
+};
+
+
+//=====================================================================
+//=====================================================================
+// Defines CBaseAllocator
+//
+// Abstract base class that manages a list of media samples
+//
+// This class provides support for getting buffers from the free list,
+// including handling of commit and (asynchronous) decommit.
+//
+// Derive from this class and override the Alloc and Free functions to
+// allocate your CMediaSample (or derived) objects and add them to the
+// free list, preparing them as necessary.
+//=====================================================================
+//=====================================================================
+
+class AM_NOVTABLE CBaseAllocator : public CUnknown,// A non delegating IUnknown
+ public IMemAllocatorCallbackTemp, // The interface we support
+ public CCritSec // Provides object locking
+{
+ class CSampleList;
+ friend class CSampleList;
+
+ /* Trick to get at protected member in CMediaSample */
+ static CMediaSample * &NextSample(__in CMediaSample *pSample)
+ {
+ return pSample->m_pNext;
+ };
+
+ /* Mini list class for the free list */
+ class CSampleList
+ {
+ public:
+ CSampleList() : m_List(NULL), m_nOnList(0) {};
+#ifdef DEBUG
+ ~CSampleList()
+ {
+ ASSERT(m_nOnList == 0);
+ };
+#endif
+ CMediaSample *Head() const { return m_List; };
+ CMediaSample *Next(__in CMediaSample *pSample) const { return CBaseAllocator::NextSample(pSample); };
+ int GetCount() const { return m_nOnList; };
+ void Add(__inout CMediaSample *pSample)
+ {
+ ASSERT(pSample != NULL);
+ CBaseAllocator::NextSample(pSample) = m_List;
+ m_List = pSample;
+ m_nOnList++;
+ };
+ CMediaSample *RemoveHead()
+ {
+ CMediaSample *pSample = m_List;
+ if (pSample != NULL) {
+ m_List = CBaseAllocator::NextSample(m_List);
+ m_nOnList--;
+ }
+ return pSample;
+ };
+ void Remove(__inout CMediaSample *pSample);
+
+ public:
+ CMediaSample *m_List;
+ int m_nOnList;
+ };
+protected:
+
+ CSampleList m_lFree; // Free list
+
+ /* Note to overriders of CBaseAllocator.
+
+ We use a lazy signalling mechanism for waiting for samples.
+ This means we don't call the OS if no waits occur.
+
+ In order to implement this:
+
+ 1. When a new sample is added to m_lFree call NotifySample() which
+ calls ReleaseSemaphore on m_hSem with a count of m_lWaiting and
+ sets m_lWaiting to 0.
+ This must all be done holding the allocator's critical section.
+
+ 2. When waiting for a sample call SetWaiting() which increments
+ m_lWaiting BEFORE leaving the allocator's critical section.
+
+ 3. Actually wait by calling WaitForSingleObject(m_hSem, INFINITE)
+ having left the allocator's critical section. The effect of
+ this is to remove 1 from the semaphore's count. You MUST call
+ this once having incremented m_lWaiting.
+
+ The following are then true when the critical section is not held :
+ (let nWaiting = number about to wait or waiting)
+
+ (1) if (m_lFree.GetCount() != 0) then (m_lWaiting == 0)
+ (2) m_lWaiting + Semaphore count == nWaiting
+
+ We would deadlock if
+ nWaiting != 0 &&
+ m_lFree.GetCount() != 0 &&
+ Semaphore count == 0
+
+ But from (1) if m_lFree.GetCount() != 0 then m_lWaiting == 0 so
+ from (2) Semaphore count == nWaiting (which is non-0) so the
+ deadlock can't happen.
+ */
+
+ HANDLE m_hSem; // For signalling
+ long m_lWaiting; // Waiting for a free element
+ long m_lCount; // how many buffers we have agreed to provide
+ long m_lAllocated; // how many buffers are currently allocated
+ long m_lSize; // agreed size of each buffer
+ long m_lAlignment; // agreed alignment
+ long m_lPrefix; // agreed prefix (preceeds GetPointer() value)
+ BOOL m_bChanged; // Have the buffer requirements changed
+
+ // if true, we are decommitted and can't allocate memory
+ BOOL m_bCommitted;
+ // if true, the decommit has happened, but we haven't called Free yet
+ // as there are still outstanding buffers
+ BOOL m_bDecommitInProgress;
+
+ // Notification interface
+ IMemAllocatorNotifyCallbackTemp *m_pNotify;
+
+ BOOL m_fEnableReleaseCallback;
+
+ // called to decommit the memory when the last buffer is freed
+ // pure virtual - need to override this
+ virtual void Free(void) PURE;
+
+ // override to allocate the memory when commit called
+ virtual HRESULT Alloc(void);
+
+public:
+
+ CBaseAllocator(
+ __in_opt LPCTSTR , __inout_opt LPUNKNOWN, __inout HRESULT *,
+ BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE);
+#ifdef UNICODE
+ CBaseAllocator(
+ __in_opt LPCSTR , __inout_opt LPUNKNOWN, __inout HRESULT *,
+ BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE);
+#endif
+ virtual ~CBaseAllocator();
+
+ DECLARE_IUNKNOWN
+
+ // override this to publicise our interfaces
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+ STDMETHODIMP SetProperties(
+ __in ALLOCATOR_PROPERTIES* pRequest,
+ __out ALLOCATOR_PROPERTIES* pActual);
+
+ // return the properties actually being used on this allocator
+ STDMETHODIMP GetProperties(
+ __out ALLOCATOR_PROPERTIES* pProps);
+
+ // override Commit to allocate memory. We handle the GetBuffer
+ //state changes
+ STDMETHODIMP Commit();
+
+ // override this to handle the memory freeing. We handle any outstanding
+ // GetBuffer calls
+ STDMETHODIMP Decommit();
+
+ // get container for a sample. Blocking, synchronous call to get the
+ // next free buffer (as represented by an IMediaSample interface).
+ // on return, the time etc properties will be invalid, but the buffer
+ // pointer and size will be correct. The two time parameters are
+ // optional and either may be NULL, they may alternatively be set to
+ // the start and end times the sample will have attached to it
+ // bPrevFramesSkipped is not used (used only by the video renderer's
+ // allocator where it affects quality management in direct draw).
+
+ STDMETHODIMP GetBuffer(__deref_out IMediaSample **ppBuffer,
+ __in_opt REFERENCE_TIME * pStartTime,
+ __in_opt REFERENCE_TIME * pEndTime,
+ DWORD dwFlags);
+
+ // final release of a CMediaSample will call this
+ STDMETHODIMP ReleaseBuffer(IMediaSample *pBuffer);
+ // obsolete:: virtual void PutOnFreeList(CMediaSample * pSample);
+
+ STDMETHODIMP SetNotify(IMemAllocatorNotifyCallbackTemp *pNotify);
+
+ STDMETHODIMP GetFreeCount(__out LONG *plBuffersFree);
+
+ // Notify that a sample is available
+ void NotifySample();
+
+ // Notify that we're waiting for a sample
+ void SetWaiting() { m_lWaiting++; };
+};
+
+
+//=====================================================================
+//=====================================================================
+// Defines CMemAllocator
+//
+// this is an allocator based on CBaseAllocator that allocates sample
+// buffers in main memory (from 'new'). You must call SetProperties
+// before calling Commit.
+//
+// we don't free the memory when going into Decommit state. The simplest
+// way to implement this without complicating CBaseAllocator is to
+// have a Free() function, called to go into decommit state, that does
+// nothing and a ReallyFree function called from our destructor that
+// actually frees the memory.
+//=====================================================================
+//=====================================================================
+
+// Make me one from quartz.dll
+STDAPI CreateMemoryAllocator(__deref_out IMemAllocator **ppAllocator);
+
+class CMemAllocator : public CBaseAllocator
+{
+
+protected:
+
+ LPBYTE m_pBuffer; // combined memory for all buffers
+
+ // override to free the memory when decommit completes
+ // - we actually do nothing, and save the memory until deletion.
+ void Free(void);
+
+ // called from the destructor (and from Alloc if changing size/count) to
+ // actually free up the memory
+ void ReallyFree(void);
+
+ // overriden to allocate the memory when commit called
+ HRESULT Alloc(void);
+
+public:
+ /* This goes in the factory template table to create new instances */
+ static CUnknown *CreateInstance(__inout_opt LPUNKNOWN, __inout HRESULT *);
+
+ STDMETHODIMP SetProperties(
+ __in ALLOCATOR_PROPERTIES* pRequest,
+ __out ALLOCATOR_PROPERTIES* pActual);
+
+ CMemAllocator(__in_opt LPCTSTR , __inout_opt LPUNKNOWN, __inout HRESULT *);
+#ifdef UNICODE
+ CMemAllocator(__in_opt LPCSTR , __inout_opt LPUNKNOWN, __inout HRESULT *);
+#endif
+ ~CMemAllocator();
+};
+
+// helper used by IAMovieSetup implementation
+STDAPI
+AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata
+ , IFilterMapper * pIFM
+ , BOOL bRegister );
+
+
+///////////////////////////////////////////////////////////////////////////
+// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
+///////////////////////////////////////////////////////////////////////////
+
+#endif /* __FILTER__ */
+
+
+
diff --git a/dshow_base/amvideo.cpp b/dshow_base/amvideo.cpp
new file mode 100644
index 0000000..42fe446
--- /dev/null
+++ b/dshow_base/amvideo.cpp
@@ -0,0 +1,275 @@
+//------------------------------------------------------------------------------
+// File: AMVideo.cpp
+//
+// Desc: DirectShow base classes - implements helper functions for
+// bitmap formats.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#include
+
+// These are bit field masks for true colour devices
+
+const DWORD bits555[] = {0x007C00,0x0003E0,0x00001F};
+const DWORD bits565[] = {0x00F800,0x0007E0,0x00001F};
+const DWORD bits888[] = {0xFF0000,0x00FF00,0x0000FF};
+
+// This maps bitmap subtypes into a bits per pixel value and also a
+// name. unicode and ansi versions are stored because we have to
+// return a pointer to a static string.
+const struct {
+ const GUID *pSubtype;
+ WORD BitCount;
+ CHAR *pName;
+ WCHAR *wszName;
+} BitCountMap[] = { &MEDIASUBTYPE_RGB1, 1, "RGB Monochrome", L"RGB Monochrome",
+ &MEDIASUBTYPE_RGB4, 4, "RGB VGA", L"RGB VGA",
+ &MEDIASUBTYPE_RGB8, 8, "RGB 8", L"RGB 8",
+ &MEDIASUBTYPE_RGB565, 16, "RGB 565 (16 bit)", L"RGB 565 (16 bit)",
+ &MEDIASUBTYPE_RGB555, 16, "RGB 555 (16 bit)", L"RGB 555 (16 bit)",
+ &MEDIASUBTYPE_RGB24, 24, "RGB 24", L"RGB 24",
+ &MEDIASUBTYPE_RGB32, 32, "RGB 32", L"RGB 32",
+ &MEDIASUBTYPE_ARGB32, 32, "ARGB 32", L"ARGB 32",
+ &MEDIASUBTYPE_Overlay, 0, "Overlay", L"Overlay",
+ &GUID_NULL, 0, "UNKNOWN", L"UNKNOWN"
+};
+
+// Return the size of the bitmap as defined by this header
+
+STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader)
+{
+ return DIBSIZE(*pHeader);
+}
+
+
+// This is called if the header has a 16 bit colour depth and needs to work
+// out the detailed type from the bit fields (either RGB 565 or RGB 555)
+
+STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader)
+{
+ BITMAPINFO *pbmInfo = (BITMAPINFO *) pbmiHeader;
+ ASSERT(pbmiHeader->biBitCount == 16);
+
+ // If its BI_RGB then it's RGB 555 by default
+
+ if (pbmiHeader->biCompression == BI_RGB) {
+ return MEDIASUBTYPE_RGB555;
+ }
+
+ // Compare the bit fields with RGB 555
+
+ DWORD *pMask = (DWORD *) pbmInfo->bmiColors;
+ if (pMask[0] == bits555[0]) {
+ if (pMask[1] == bits555[1]) {
+ if (pMask[2] == bits555[2]) {
+ return MEDIASUBTYPE_RGB555;
+ }
+ }
+ }
+
+ // Compare the bit fields with RGB 565
+
+ pMask = (DWORD *) pbmInfo->bmiColors;
+ if (pMask[0] == bits565[0]) {
+ if (pMask[1] == bits565[1]) {
+ if (pMask[2] == bits565[2]) {
+ return MEDIASUBTYPE_RGB565;
+ }
+ }
+ }
+ return GUID_NULL;
+}
+
+
+// Given a BITMAPINFOHEADER structure this returns the GUID sub type that is
+// used to describe it in format negotiations. For example a video codec fills
+// in the format block with a VIDEOINFO structure, it also fills in the major
+// type with MEDIATYPE_VIDEO and the subtype with a GUID that matches the bit
+// count, for example if it is an eight bit image then MEDIASUBTYPE_RGB8
+
+STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader)
+{
+ ASSERT(pbmiHeader);
+
+ // If it's not RGB then create a GUID from the compression type
+
+ if (pbmiHeader->biCompression != BI_RGB) {
+ if (pbmiHeader->biCompression != BI_BITFIELDS) {
+ FOURCCMap FourCCMap(pbmiHeader->biCompression);
+ return (const GUID) FourCCMap;
+ }
+ }
+
+ // Map the RGB DIB bit depth to a image GUID
+
+ switch(pbmiHeader->biBitCount) {
+ case 1 : return MEDIASUBTYPE_RGB1;
+ case 4 : return MEDIASUBTYPE_RGB4;
+ case 8 : return MEDIASUBTYPE_RGB8;
+ case 16 : return GetTrueColorType(pbmiHeader);
+ case 24 : return MEDIASUBTYPE_RGB24;
+ case 32 : return MEDIASUBTYPE_RGB32;
+ }
+ return GUID_NULL;
+}
+
+
+// Given a video bitmap subtype we return the number of bits per pixel it uses
+// We return a WORD bit count as thats what the BITMAPINFOHEADER uses. If the
+// GUID subtype is not found in the table we return an invalid USHRT_MAX
+
+STDAPI_(WORD) GetBitCount(const GUID *pSubtype)
+{
+ ASSERT(pSubtype);
+ const GUID *pMediaSubtype;
+ INT iPosition = 0;
+
+ // Scan the mapping list seeing if the source GUID matches any known
+ // bitmap subtypes, the list is terminated by a GUID_NULL entry
+
+ while (TRUE) {
+ pMediaSubtype = BitCountMap[iPosition].pSubtype;
+ if (IsEqualGUID(*pMediaSubtype,GUID_NULL)) {
+ return USHRT_MAX;
+ }
+ if (IsEqualGUID(*pMediaSubtype,*pSubtype)) {
+ return BitCountMap[iPosition].BitCount;
+ }
+ iPosition++;
+ }
+}
+
+
+// Given a bitmap subtype we return a description name that can be used for
+// debug purposes. In a retail build this function still returns the names
+// If the subtype isn't found in the lookup table we return string UNKNOWN
+
+int LocateSubtype(const GUID *pSubtype)
+{
+ ASSERT(pSubtype);
+ const GUID *pMediaSubtype;
+ INT iPosition = 0;
+
+ // Scan the mapping list seeing if the source GUID matches any known
+ // bitmap subtypes, the list is terminated by a GUID_NULL entry
+
+ while (TRUE) {
+ pMediaSubtype = BitCountMap[iPosition].pSubtype;
+ if (IsEqualGUID(*pMediaSubtype,*pSubtype) ||
+ IsEqualGUID(*pMediaSubtype,GUID_NULL)
+ )
+ {
+ break;
+ }
+
+ iPosition++;
+ }
+
+ return iPosition;
+}
+
+
+
+STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype)
+{
+ return BitCountMap[LocateSubtype(pSubtype)].wszName;
+}
+
+STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype)
+{
+ return BitCountMap[LocateSubtype(pSubtype)].pName;
+}
+
+#ifndef GetSubtypeName
+#error wxutil.h should have defined GetSubtypeName
+#endif
+#undef GetSubtypeName
+
+// this is here for people that linked to it directly; most people
+// would use the header file that picks the A or W version.
+STDAPI_(CHAR *) GetSubtypeName(const GUID *pSubtype)
+{
+ return GetSubtypeNameA(pSubtype);
+}
+
+
+// The mechanism for describing a bitmap format is with the BITMAPINFOHEADER
+// This is really messy to deal with because it invariably has fields that
+// follow it holding bit fields, palettes and the rest. This function gives
+// the number of bytes required to hold a VIDEOINFO that represents it. This
+// count includes the prefix information (like the rcSource rectangle) the
+// BITMAPINFOHEADER field, and any other colour information on the end.
+//
+// WARNING If you want to copy a BITMAPINFOHEADER into a VIDEOINFO always make
+// sure that you use the HEADER macro because the BITMAPINFOHEADER field isn't
+// right at the start of the VIDEOINFO (there are a number of other fields),
+//
+// CopyMemory(HEADER(pVideoInfo),pbmi,sizeof(BITMAPINFOHEADER));
+//
+
+STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader)
+{
+ // Everyone has this to start with this
+ LONG Size = SIZE_PREHEADER + pHeader->biSize;
+
+ ASSERT(pHeader->biSize >= sizeof(BITMAPINFOHEADER));
+
+ // Does this format use a palette, if the number of colours actually used
+ // is zero then it is set to the maximum that are allowed for that colour
+ // depth (an example is 256 for eight bits). Truecolour formats may also
+ // pass a palette with them in which case the used count is non zero
+
+ // This would scare me.
+ ASSERT(pHeader->biBitCount <= iPALETTE || pHeader->biClrUsed == 0);
+
+ if (pHeader->biBitCount <= iPALETTE || pHeader->biClrUsed) {
+ LONG Entries = (DWORD) 1 << pHeader->biBitCount;
+ if (pHeader->biClrUsed) {
+ Entries = pHeader->biClrUsed;
+ }
+ Size += Entries * sizeof(RGBQUAD);
+ }
+
+ // Truecolour formats may have a BI_BITFIELDS specifier for compression
+ // type which means that room for three DWORDs should be allocated that
+ // specify where in each pixel the RGB colour components may be found
+
+ if (pHeader->biCompression == BI_BITFIELDS) {
+ Size += SIZE_MASKS;
+ }
+
+ // A BITMAPINFO for a palettised image may also contain a palette map that
+ // provides the information to map from a source palette to a destination
+ // palette during a BitBlt for example, because this information is only
+ // ever processed during drawing you don't normally store the palette map
+ // nor have any way of knowing if it is present in the data structure
+
+ return Size;
+}
+
+
+// Returns TRUE if the VIDEOINFO contains a palette
+
+STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo)
+{
+ if (PALETTISED(pVideoInfo) == FALSE) {
+ if (pVideoInfo->bmiHeader.biClrUsed == 0) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+// Return a pointer to the first entry in a palette
+
+STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo)
+{
+ if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) {
+ return TRUECOLOR(pVideoInfo)->bmiColors;
+ }
+ return COLORS(pVideoInfo);
+}
diff --git a/dshow_base/arithutil.cpp b/dshow_base/arithutil.cpp
new file mode 100644
index 0000000..cd0d127
--- /dev/null
+++ b/dshow_base/arithutil.cpp
@@ -0,0 +1,360 @@
+//------------------------------------------------------------------------------
+// File: ArithUtil.cpp
+//
+// Desc: DirectShow base classes - implements helper classes for building
+// multimedia filters.
+//
+// Copyright (c) 1992-2004 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+#include
+
+//
+// Declare function from largeint.h we need so that PPC can build
+//
+
+//
+// Enlarged integer divide - 64-bits / 32-bits > 32-bits
+//
+
+#ifndef _X86_
+
+#define LLtoU64(x) (*(unsigned __int64*)(void*)(&(x)))
+
+__inline
+ULONG
+WINAPI
+EnlargedUnsignedDivide (
+ IN ULARGE_INTEGER Dividend,
+ IN ULONG Divisor,
+ IN PULONG Remainder
+ )
+{
+ // return remainder if necessary
+ if (Remainder != NULL)
+ *Remainder = (ULONG)(LLtoU64(Dividend) % Divisor);
+ return (ULONG)(LLtoU64(Dividend) / Divisor);
+}
+
+#else
+__inline
+ULONG
+WINAPI
+EnlargedUnsignedDivide (
+ IN ULARGE_INTEGER Dividend,
+ IN ULONG Divisor,
+ IN PULONG Remainder
+ )
+{
+ ULONG ulResult;
+ _asm {
+ mov eax,Dividend.LowPart
+ mov edx,Dividend.HighPart
+ mov ecx,Remainder
+ div Divisor
+ or ecx,ecx
+ jz short label
+ mov [ecx],edx
+label:
+ mov ulResult,eax
+ }
+ return ulResult;
+}
+#endif
+
+
+/* Arithmetic functions to help with time format conversions
+*/
+
+#ifdef _M_ALPHA
+// work around bug in version 12.00.8385 of the alpha compiler where
+// UInt32x32To64 sign-extends its arguments (?)
+#undef UInt32x32To64
+#define UInt32x32To64(a, b) (((ULONGLONG)((ULONG)(a)) & 0xffffffff) * ((ULONGLONG)((ULONG)(b)) & 0xffffffff))
+#endif
+
+/* Compute (a * b + d) / c */
+LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG d)
+{
+ /* Compute the absolute values to avoid signed arithmetic problems */
+ ULARGE_INTEGER ua, ub;
+ DWORDLONG uc;
+
+ ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a);
+ ub.QuadPart = (DWORDLONG)(b >= 0 ? b : -b);
+ uc = (DWORDLONG)(c >= 0 ? c : -c);
+ BOOL bSign = (a < 0) ^ (b < 0);
+
+ /* Do long multiplication */
+ ULARGE_INTEGER p[2];
+ p[0].QuadPart = UInt32x32To64(ua.LowPart, ub.LowPart);
+
+ /* This next computation cannot overflow into p[1].HighPart because
+ the max number we can compute here is:
+
+ (2 ** 32 - 1) * (2 ** 32 - 1) + // ua.LowPart * ub.LowPart
+ (2 ** 32) * (2 ** 31) * (2 ** 32 - 1) * 2 // x.LowPart * y.HighPart * 2
+
+ == 2 ** 96 - 2 ** 64 + (2 ** 64 - 2 ** 33 + 1)
+ == 2 ** 96 - 2 ** 33 + 1
+ < 2 ** 96
+ */
+
+ ULARGE_INTEGER x;
+ x.QuadPart = UInt32x32To64(ua.LowPart, ub.HighPart) +
+ UInt32x32To64(ua.HighPart, ub.LowPart) +
+ p[0].HighPart;
+ p[0].HighPart = x.LowPart;
+ p[1].QuadPart = UInt32x32To64(ua.HighPart, ub.HighPart) + x.HighPart;
+
+ if (d != 0) {
+ ULARGE_INTEGER ud[2];
+ if (bSign) {
+ ud[0].QuadPart = (DWORDLONG)(-d);
+ if (d > 0) {
+ /* -d < 0 */
+ ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1;
+ } else {
+ ud[1].QuadPart = (DWORDLONG)0;
+ }
+ } else {
+ ud[0].QuadPart = (DWORDLONG)d;
+ if (d < 0) {
+ ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1;
+ } else {
+ ud[1].QuadPart = (DWORDLONG)0;
+ }
+ }
+ /* Now do extended addition */
+ ULARGE_INTEGER uliTotal;
+
+ /* Add ls DWORDs */
+ uliTotal.QuadPart = (DWORDLONG)ud[0].LowPart + p[0].LowPart;
+ p[0].LowPart = uliTotal.LowPart;
+
+ /* Propagate carry */
+ uliTotal.LowPart = uliTotal.HighPart;
+ uliTotal.HighPart = 0;
+
+ /* Add 2nd most ls DWORDs */
+ uliTotal.QuadPart += (DWORDLONG)ud[0].HighPart + p[0].HighPart;
+ p[0].HighPart = uliTotal.LowPart;
+
+ /* Propagate carry */
+ uliTotal.LowPart = uliTotal.HighPart;
+ uliTotal.HighPart = 0;
+
+ /* Add MS DWORDLONGs - no carry expected */
+ p[1].QuadPart += ud[1].QuadPart + uliTotal.QuadPart;
+
+ /* Now see if we got a sign change from the addition */
+ if ((LONG)p[1].HighPart < 0) {
+ bSign = !bSign;
+
+ /* Negate the current value (ugh!) */
+ p[0].QuadPart = ~p[0].QuadPart;
+ p[1].QuadPart = ~p[1].QuadPart;
+ p[0].QuadPart += 1;
+ p[1].QuadPart += (p[0].QuadPart == 0);
+ }
+ }
+
+ /* Now for the division */
+ if (c < 0) {
+ bSign = !bSign;
+ }
+
+
+ /* This will catch c == 0 and overflow */
+ if (uc <= p[1].QuadPart) {
+ return bSign ? (LONGLONG)0x8000000000000000 :
+ (LONGLONG)0x7FFFFFFFFFFFFFFF;
+ }
+
+ DWORDLONG ullResult;
+
+ /* Do the division */
+ /* If the dividend is a DWORD_LONG use the compiler */
+ if (p[1].QuadPart == 0) {
+ ullResult = p[0].QuadPart / uc;
+ return bSign ? -(LONGLONG)ullResult : (LONGLONG)ullResult;
+ }
+
+ /* If the divisor is a DWORD then its simpler */
+ ULARGE_INTEGER ulic;
+ ulic.QuadPart = uc;
+ if (ulic.HighPart == 0) {
+ ULARGE_INTEGER uliDividend;
+ ULARGE_INTEGER uliResult;
+ DWORD dwDivisor = (DWORD)uc;
+ // ASSERT(p[1].HighPart == 0 && p[1].LowPart < dwDivisor);
+ uliDividend.HighPart = p[1].LowPart;
+ uliDividend.LowPart = p[0].HighPart;
+#ifndef USE_LARGEINT
+ uliResult.HighPart = (DWORD)(uliDividend.QuadPart / dwDivisor);
+ p[0].HighPart = (DWORD)(uliDividend.QuadPart % dwDivisor);
+ uliResult.LowPart = 0;
+ uliResult.QuadPart = p[0].QuadPart / dwDivisor + uliResult.QuadPart;
+#else
+ /* NOTE - this routine will take exceptions if
+ the result does not fit in a DWORD
+ */
+ if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) {
+ uliResult.HighPart = EnlargedUnsignedDivide(
+ uliDividend,
+ dwDivisor,
+ &p[0].HighPart);
+ } else {
+ uliResult.HighPart = 0;
+ }
+ uliResult.LowPart = EnlargedUnsignedDivide(
+ p[0],
+ dwDivisor,
+ NULL);
+#endif
+ return bSign ? -(LONGLONG)uliResult.QuadPart :
+ (LONGLONG)uliResult.QuadPart;
+ }
+
+
+ ullResult = 0;
+
+ /* OK - do long division */
+ for (int i = 0; i < 64; i++) {
+ ullResult <<= 1;
+
+ /* Shift 128 bit p left 1 */
+ p[1].QuadPart <<= 1;
+ if ((p[0].HighPart & 0x80000000) != 0) {
+ p[1].LowPart++;
+ }
+ p[0].QuadPart <<= 1;
+
+ /* Compare */
+ if (uc <= p[1].QuadPart) {
+ p[1].QuadPart -= uc;
+ ullResult += 1;
+ }
+ }
+
+ return bSign ? - (LONGLONG)ullResult : (LONGLONG)ullResult;
+}
+
+LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG d)
+{
+ ULARGE_INTEGER ua;
+ DWORD ub;
+ DWORD uc;
+
+ /* Compute the absolute values to avoid signed arithmetic problems */
+ ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a);
+ ub = (DWORD)(b >= 0 ? b : -b);
+ uc = (DWORD)(c >= 0 ? c : -c);
+ BOOL bSign = (a < 0) ^ (b < 0);
+
+ /* Do long multiplication */
+ ULARGE_INTEGER p0;
+ DWORD p1;
+ p0.QuadPart = UInt32x32To64(ua.LowPart, ub);
+
+ if (ua.HighPart != 0) {
+ ULARGE_INTEGER x;
+ x.QuadPart = UInt32x32To64(ua.HighPart, ub) + p0.HighPart;
+ p0.HighPart = x.LowPart;
+ p1 = x.HighPart;
+ } else {
+ p1 = 0;
+ }
+
+ if (d != 0) {
+ ULARGE_INTEGER ud0;
+ DWORD ud1;
+
+ if (bSign) {
+ //
+ // Cast d to LONGLONG first otherwise -0x80000000 sign extends
+ // incorrectly
+ //
+ ud0.QuadPart = (DWORDLONG)(-(LONGLONG)d);
+ if (d > 0) {
+ /* -d < 0 */
+ ud1 = (DWORD)-1;
+ } else {
+ ud1 = (DWORD)0;
+ }
+ } else {
+ ud0.QuadPart = (DWORDLONG)d;
+ if (d < 0) {
+ ud1 = (DWORD)-1;
+ } else {
+ ud1 = (DWORD)0;
+ }
+ }
+ /* Now do extended addition */
+ ULARGE_INTEGER uliTotal;
+
+ /* Add ls DWORDs */
+ uliTotal.QuadPart = (DWORDLONG)ud0.LowPart + p0.LowPart;
+ p0.LowPart = uliTotal.LowPart;
+
+ /* Propagate carry */
+ uliTotal.LowPart = uliTotal.HighPart;
+ uliTotal.HighPart = 0;
+
+ /* Add 2nd most ls DWORDs */
+ uliTotal.QuadPart += (DWORDLONG)ud0.HighPart + p0.HighPart;
+ p0.HighPart = uliTotal.LowPart;
+
+ /* Add MS DWORDLONGs - no carry expected */
+ p1 += ud1 + uliTotal.HighPart;
+
+ /* Now see if we got a sign change from the addition */
+ if ((LONG)p1 < 0) {
+ bSign = !bSign;
+
+ /* Negate the current value (ugh!) */
+ p0.QuadPart = ~p0.QuadPart;
+ p1 = ~p1;
+ p0.QuadPart += 1;
+ p1 += (p0.QuadPart == 0);
+ }
+ }
+
+ /* Now for the division */
+ if (c < 0) {
+ bSign = !bSign;
+ }
+
+
+ /* This will catch c == 0 and overflow */
+ if (uc <= p1) {
+ return bSign ? (LONGLONG)0x8000000000000000 :
+ (LONGLONG)0x7FFFFFFFFFFFFFFF;
+ }
+
+ /* Do the division */
+
+ /* If the divisor is a DWORD then its simpler */
+ ULARGE_INTEGER uliDividend;
+ ULARGE_INTEGER uliResult;
+ DWORD dwDivisor = uc;
+ uliDividend.HighPart = p1;
+ uliDividend.LowPart = p0.HighPart;
+ /* NOTE - this routine will take exceptions if
+ the result does not fit in a DWORD
+ */
+ if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) {
+ uliResult.HighPart = EnlargedUnsignedDivide(
+ uliDividend,
+ dwDivisor,
+ &p0.HighPart);
+ } else {
+ uliResult.HighPart = 0;
+ }
+ uliResult.LowPart = EnlargedUnsignedDivide(
+ p0,
+ dwDivisor,
+ NULL);
+ return bSign ? -(LONGLONG)uliResult.QuadPart :
+ (LONGLONG)uliResult.QuadPart;
+}
diff --git a/dshow_base/baseclasses.sln b/dshow_base/baseclasses.sln
new file mode 100644
index 0000000..0107934
--- /dev/null
+++ b/dshow_base/baseclasses.sln
@@ -0,0 +1,38 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BaseClasses", "BaseClasses.vcxproj", "{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug_MBCS|Win32 = Debug_MBCS|Win32
+ Debug_MBCS|x64 = Debug_MBCS|x64
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release_MBCS|Win32 = Release_MBCS|Win32
+ Release_MBCS|x64 = Release_MBCS|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug_MBCS|Win32.ActiveCfg = Debug_MBCS|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug_MBCS|Win32.Build.0 = Debug_MBCS|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug_MBCS|x64.ActiveCfg = Debug_MBCS|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug_MBCS|x64.Build.0 = Debug_MBCS|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|Win32.Build.0 = Debug|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|x64.ActiveCfg = Debug|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|x64.Build.0 = Debug|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release_MBCS|Win32.ActiveCfg = Release_MBCS|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release_MBCS|Win32.Build.0 = Release_MBCS|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release_MBCS|x64.ActiveCfg = Release_MBCS|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release_MBCS|x64.Build.0 = Release_MBCS|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|Win32.ActiveCfg = Release|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|Win32.Build.0 = Release|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|x64.ActiveCfg = Release|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/dshow_base/baseclasses.sln.old b/dshow_base/baseclasses.sln.old
new file mode 100644
index 0000000..a93b581
--- /dev/null
+++ b/dshow_base/baseclasses.sln.old
@@ -0,0 +1,38 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BaseClasses", "BaseClasses.vcproj", "{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug_MBCS|Win32 = Debug_MBCS|Win32
+ Debug_MBCS|x64 = Debug_MBCS|x64
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release_MBCS|Win32 = Release_MBCS|Win32
+ Release_MBCS|x64 = Release_MBCS|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug_MBCS|Win32.ActiveCfg = Debug_MBCS|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug_MBCS|Win32.Build.0 = Debug_MBCS|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug_MBCS|x64.ActiveCfg = Debug_MBCS|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug_MBCS|x64.Build.0 = Debug_MBCS|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|Win32.Build.0 = Debug|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|x64.ActiveCfg = Debug|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Debug|x64.Build.0 = Debug|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release_MBCS|Win32.ActiveCfg = Release_MBCS|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release_MBCS|Win32.Build.0 = Release_MBCS|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release_MBCS|x64.ActiveCfg = Release_MBCS|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release_MBCS|x64.Build.0 = Release_MBCS|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|Win32.ActiveCfg = Release|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|Win32.Build.0 = Release|Win32
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|x64.ActiveCfg = Release|x64
+ {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/dshow_base/cache.h b/dshow_base/cache.h
index 2ef996f..0a807c2 100644
--- a/dshow_base/cache.h
+++ b/dshow_base/cache.h
@@ -1,74 +1,74 @@
-//------------------------------------------------------------------------------
-// File: Cache.h
-//
-// Desc: DirectShow base classes - efines a non-MFC generic cache class.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-/* This class implements a simple cache. A cache object is instantiated
- with the number of items it is to hold. An item is a pointer to an
- object derived from CBaseObject (helps reduce memory leaks). The cache
- can then have objects added to it and removed from it. The cache size
- is fixed at construction time and may therefore run out or be flooded.
- If it runs out it returns a NULL pointer, if it fills up it also returns
- a NULL pointer instead of a pointer to the object just inserted */
-
-/* Making these classes inherit from CBaseObject does nothing for their
- functionality but it allows us to check there are no memory leaks */
-
-/* WARNING Be very careful when using this class, what it lets you do is
- store and retrieve objects so that you can minimise object creation
- which in turns improves efficiency. However the object you store is
- exactly the same as the object you get back which means that it short
- circuits the constructor initialisation phase. This means any class
- variables the object has (eg pointers) are highly likely to be invalid.
- Therefore ensure you reinitialise the object before using it again */
-
-
-#ifndef __CACHE__
-#define __CACHE__
-
-
-class CCache : CBaseObject {
-
- /* Make copy constructor and assignment operator inaccessible */
-
- CCache(const CCache &refCache);
- CCache &operator=(const CCache &refCache);
-
-private:
-
- /* These are initialised in the constructor. The first variable points to
- an array of pointers, each of which points to a CBaseObject derived
- object. The m_iCacheSize is the static fixed size for the cache and the
- m_iUsed defines the number of places filled with objects at any time.
- We fill the array of pointers from the start (ie m_ppObjects[0] first)
- and then only add and remove objects from the end position, so in this
- respect the array of object pointers should be treated as a stack */
-
- CBaseObject **m_ppObjects;
- const INT m_iCacheSize;
- INT m_iUsed;
-
-public:
-
- CCache(TCHAR *pName,INT iItems);
- virtual ~CCache();
-
- /* Add an item to the cache */
- CBaseObject *AddToCache(CBaseObject *pObject);
-
- /* Remove an item from the cache */
- CBaseObject *RemoveFromCache();
-
- /* Delete all the objects held in the cache */
- void RemoveAll(void);
-
- /* Return the cache size which is set during construction */
- INT GetCacheSize(void) const {return m_iCacheSize;};
-};
-
-#endif /* __CACHE__ */
-
+//------------------------------------------------------------------------------
+// File: Cache.h
+//
+// Desc: DirectShow base classes - efines a non-MFC generic cache class.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+/* This class implements a simple cache. A cache object is instantiated
+ with the number of items it is to hold. An item is a pointer to an
+ object derived from CBaseObject (helps reduce memory leaks). The cache
+ can then have objects added to it and removed from it. The cache size
+ is fixed at construction time and may therefore run out or be flooded.
+ If it runs out it returns a NULL pointer, if it fills up it also returns
+ a NULL pointer instead of a pointer to the object just inserted */
+
+/* Making these classes inherit from CBaseObject does nothing for their
+ functionality but it allows us to check there are no memory leaks */
+
+/* WARNING Be very careful when using this class, what it lets you do is
+ store and retrieve objects so that you can minimise object creation
+ which in turns improves efficiency. However the object you store is
+ exactly the same as the object you get back which means that it short
+ circuits the constructor initialisation phase. This means any class
+ variables the object has (eg pointers) are highly likely to be invalid.
+ Therefore ensure you reinitialise the object before using it again */
+
+
+#ifndef __CACHE__
+#define __CACHE__
+
+
+class CCache : CBaseObject {
+
+ /* Make copy constructor and assignment operator inaccessible */
+
+ CCache(const CCache &refCache);
+ CCache &operator=(const CCache &refCache);
+
+private:
+
+ /* These are initialised in the constructor. The first variable points to
+ an array of pointers, each of which points to a CBaseObject derived
+ object. The m_iCacheSize is the static fixed size for the cache and the
+ m_iUsed defines the number of places filled with objects at any time.
+ We fill the array of pointers from the start (ie m_ppObjects[0] first)
+ and then only add and remove objects from the end position, so in this
+ respect the array of object pointers should be treated as a stack */
+
+ CBaseObject **m_ppObjects;
+ const INT m_iCacheSize;
+ INT m_iUsed;
+
+public:
+
+ CCache(__in_opt LPCTSTR pName,INT iItems);
+ virtual ~CCache();
+
+ /* Add an item to the cache */
+ CBaseObject *AddToCache(__in CBaseObject *pObject);
+
+ /* Remove an item from the cache */
+ CBaseObject *RemoveFromCache();
+
+ /* Delete all the objects held in the cache */
+ void RemoveAll(void);
+
+ /* Return the cache size which is set during construction */
+ INT GetCacheSize(void) const {return m_iCacheSize;};
+};
+
+#endif /* __CACHE__ */
+
diff --git a/dshow_base/checkbmi.h b/dshow_base/checkbmi.h
new file mode 100644
index 0000000..7287967
--- /dev/null
+++ b/dshow_base/checkbmi.h
@@ -0,0 +1,120 @@
+// Copyright (c) 1992 - 1997 Microsoft Corporation. All Rights Reserved.
+
+#ifndef _CHECKBMI_H_
+#define _CHECKBMI_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Helper
+__inline BOOL MultiplyCheckOverflow(DWORD a, DWORD b, __deref_out_range(==, a * b) DWORD *pab) {
+ *pab = a * b;
+ if ((a == 0) || (((*pab) / a) == b)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+// Checks if the fields in a BITMAPINFOHEADER won't generate
+// overlows and buffer overruns
+// This is not a complete check and does not guarantee code using this structure will be secure
+// from attack
+// Bugs this is guarding against:
+// 1. Total structure size calculation overflowing
+// 2. biClrUsed > 256 for 8-bit palettized content
+// 3. Total bitmap size in bytes overflowing
+// 4. biSize < size of the base structure leading to accessessing random memory
+// 5. Total structure size exceeding know size of data
+//
+
+__success(return != 0) __inline BOOL ValidateBitmapInfoHeader(
+ const BITMAPINFOHEADER *pbmi, // pointer to structure to check
+ __out_range(>=, sizeof(BITMAPINFOHEADER)) DWORD cbSize // size of memory block containing structure
+)
+{
+ DWORD dwWidthInBytes;
+ DWORD dwBpp;
+ DWORD dwWidthInBits;
+ DWORD dwHeight;
+ DWORD dwSizeImage;
+ DWORD dwClrUsed;
+
+ // Reject bad parameters - do the size check first to avoid reading bad memory
+ if (cbSize < sizeof(BITMAPINFOHEADER) ||
+ pbmi->biSize < sizeof(BITMAPINFOHEADER) ||
+ pbmi->biSize > 4096) {
+ return FALSE;
+ }
+
+ // Reject 0 size
+ if (pbmi->biWidth == 0 || pbmi->biHeight == 0) {
+ return FALSE;
+ }
+
+ // Use bpp of 200 for validating against further overflows if not set for compressed format
+ dwBpp = 200;
+
+ if (pbmi->biBitCount > dwBpp) {
+ return FALSE;
+ }
+
+ // Strictly speaking abs can overflow so cast explicitly to DWORD
+ dwHeight = (DWORD)abs(pbmi->biHeight);
+
+ if (!MultiplyCheckOverflow(dwBpp, (DWORD)pbmi->biWidth, &dwWidthInBits)) {
+ return FALSE;
+ }
+
+ // Compute correct width in bytes - rounding up to 4 bytes
+ dwWidthInBytes = (dwWidthInBits / 8 + 3) & ~3;
+
+ if (!MultiplyCheckOverflow(dwWidthInBytes, dwHeight, &dwSizeImage)) {
+ return FALSE;
+ }
+
+ // Fail if total size is 0 - this catches indivual quantities being 0
+ // Also don't allow huge values > 1GB which might cause arithmetic
+ // errors for users
+ if (dwSizeImage > 0x40000000 ||
+ pbmi->biSizeImage > 0x40000000) {
+ return FALSE;
+ }
+
+ // Fail if biClrUsed looks bad
+ if (pbmi->biClrUsed > 256) {
+ return FALSE;
+ }
+
+ if (pbmi->biClrUsed == 0 && pbmi->biBitCount <= 8 && pbmi->biBitCount > 0) {
+ dwClrUsed = (1 << pbmi->biBitCount);
+ } else {
+ dwClrUsed = pbmi->biClrUsed;
+ }
+
+ // Check total size
+ if (cbSize < pbmi->biSize + dwClrUsed * sizeof(RGBQUAD) +
+ (pbmi->biCompression == BI_BITFIELDS ? 3 * sizeof(DWORD) : 0)) {
+ return FALSE;
+ }
+
+ // If it is RGB validate biSizeImage - lots of code assumes the size is correct
+ if (pbmi->biCompression == BI_RGB || pbmi->biCompression == BI_BITFIELDS) {
+ if (pbmi->biSizeImage != 0) {
+ DWORD dwBits = (DWORD)pbmi->biWidth * (DWORD)pbmi->biBitCount;
+ DWORD dwWidthInBytes = ((DWORD)((dwBits+31) & (~31)) / 8);
+ DWORD dwTotalSize = (DWORD)abs(pbmi->biHeight) * dwWidthInBytes;
+ if (dwTotalSize > pbmi->biSizeImage) {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _CHECKBMI_H_
diff --git a/dshow_base/combase.cpp b/dshow_base/combase.cpp
new file mode 100644
index 0000000..ec62a88
--- /dev/null
+++ b/dshow_base/combase.cpp
@@ -0,0 +1,265 @@
+//------------------------------------------------------------------------------
+// File: ComBase.cpp
+//
+// Desc: DirectShow base classes - implements class hierarchy for creating
+// COM objects.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#pragma warning( disable : 4514 ) // Disable warnings re unused inline functions
+
+
+/* Define the static member variable */
+
+LONG CBaseObject::m_cObjects = 0;
+
+
+/* Constructor */
+
+CBaseObject::CBaseObject(__in_opt LPCTSTR pName)
+{
+ /* Increment the number of active objects */
+ InterlockedIncrement(&m_cObjects);
+
+#ifdef DEBUG
+
+#ifdef UNICODE
+ m_dwCookie = DbgRegisterObjectCreation(0, pName);
+#else
+ m_dwCookie = DbgRegisterObjectCreation(pName, 0);
+#endif
+
+#endif
+}
+
+#ifdef UNICODE
+CBaseObject::CBaseObject(const char *pName)
+{
+ /* Increment the number of active objects */
+ InterlockedIncrement(&m_cObjects);
+
+#ifdef DEBUG
+ m_dwCookie = DbgRegisterObjectCreation(pName, 0);
+#endif
+}
+#endif
+
+HINSTANCE hlibOLEAut32;
+
+/* Destructor */
+
+CBaseObject::~CBaseObject()
+{
+ /* Decrement the number of objects active */
+ if (InterlockedDecrement(&m_cObjects) == 0) {
+ if (hlibOLEAut32) {
+ FreeLibrary(hlibOLEAut32);
+
+ hlibOLEAut32 = 0;
+ }
+ };
+
+
+#ifdef DEBUG
+ DbgRegisterObjectDestruction(m_dwCookie);
+#endif
+}
+
+static const TCHAR szOle32Aut[] = TEXT("OleAut32.dll");
+
+HINSTANCE LoadOLEAut32()
+{
+ if (hlibOLEAut32 == 0) {
+
+ hlibOLEAut32 = LoadLibrary(szOle32Aut);
+ }
+
+ return hlibOLEAut32;
+}
+
+
+/* Constructor */
+
+// We know we use "this" in the initialization list, we also know we don't modify *phr.
+#pragma warning( disable : 4355 4100 )
+CUnknown::CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk)
+: CBaseObject(pName)
+/* Start the object with a reference count of zero - when the */
+/* object is queried for it's first interface this may be */
+/* incremented depending on whether or not this object is */
+/* currently being aggregated upon */
+, m_cRef(0)
+/* Set our pointer to our IUnknown interface. */
+/* If we have an outer, use its, otherwise use ours. */
+/* This pointer effectivly points to the owner of */
+/* this object and can be accessed by the GetOwner() method. */
+, m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast( static_cast(this) ) )
+ /* Why the double cast? Well, the inner cast is a type-safe cast */
+ /* to pointer to a type from which we inherit. The second is */
+ /* type-unsafe but works because INonDelegatingUnknown "behaves */
+ /* like" IUnknown. (Only the names on the methods change.) */
+{
+ // Everything we need to do has been done in the initializer list
+}
+
+// This does the same as above except it has a useless HRESULT argument
+// use the previous constructor, this is just left for compatibility...
+CUnknown::CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) :
+ CBaseObject(pName),
+ m_cRef(0),
+ m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast( static_cast(this) ) )
+{
+}
+
+#ifdef UNICODE
+CUnknown::CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk)
+: CBaseObject(pName), m_cRef(0),
+ m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast( static_cast(this) ) )
+{ }
+
+CUnknown::CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) :
+ CBaseObject(pName), m_cRef(0),
+ m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast( static_cast(this) ) )
+{ }
+
+#endif
+
+#pragma warning( default : 4355 4100 )
+
+
+/* QueryInterface */
+
+STDMETHODIMP CUnknown::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)
+{
+ CheckPointer(ppv,E_POINTER);
+ ValidateReadWritePtr(ppv,sizeof(PVOID));
+
+ /* We know only about IUnknown */
+
+ if (riid == IID_IUnknown) {
+ GetInterface((LPUNKNOWN) (PNDUNKNOWN) this, ppv);
+ return NOERROR;
+ } else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+/* We have to ensure that we DON'T use a max macro, since these will typically */
+/* lead to one of the parameters being evaluated twice. Since we are worried */
+/* about concurrency, we can't afford to access the m_cRef twice since we can't */
+/* afford to run the risk that its value having changed between accesses. */
+
+template inline static T ourmax( const T & a, const T & b )
+{
+ return a > b ? a : b;
+}
+
+/* AddRef */
+
+STDMETHODIMP_(ULONG) CUnknown::NonDelegatingAddRef()
+{
+ LONG lRef = InterlockedIncrement( &m_cRef );
+ ASSERT(lRef > 0);
+ DbgLog((LOG_MEMORY,3,TEXT(" Obj %d ref++ = %d"),
+ m_dwCookie, m_cRef));
+ return ourmax(ULONG(m_cRef), 1ul);
+}
+
+
+/* Release */
+
+STDMETHODIMP_(ULONG) CUnknown::NonDelegatingRelease()
+{
+ /* If the reference count drops to zero delete ourselves */
+
+ LONG lRef = InterlockedDecrement( &m_cRef );
+ ASSERT(lRef >= 0);
+
+ DbgLog((LOG_MEMORY,3,TEXT(" Object %d ref-- = %d"),
+ m_dwCookie, m_cRef));
+ if (lRef == 0) {
+
+ // COM rules say we must protect against re-entrancy.
+ // If we are an aggregator and we hold our own interfaces
+ // on the aggregatee, the QI for these interfaces will
+ // addref ourselves. So after doing the QI we must release
+ // a ref count on ourselves. Then, before releasing the
+ // private interface, we must addref ourselves. When we do
+ // this from the destructor here it will result in the ref
+ // count going to 1 and then back to 0 causing us to
+ // re-enter the destructor. Hence we add an extra refcount here
+ // once we know we will delete the object.
+ // for an example aggregator see filgraph\distrib.cpp.
+
+ m_cRef++;
+
+ delete this;
+ return ULONG(0);
+ } else {
+ // Don't touch m_cRef again even in this leg as the object
+ // may have just been released on another thread too
+ return ourmax(ULONG(lRef), 1ul);
+ }
+}
+
+
+/* Return an interface pointer to a requesting client
+ performing a thread safe AddRef as necessary */
+
+STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv)
+{
+ CheckPointer(ppv, E_POINTER);
+ *ppv = pUnk;
+ pUnk->AddRef();
+ return NOERROR;
+}
+
+
+/* Compares two interfaces and returns TRUE if they are on the same object */
+
+BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond)
+{
+ /* Different objects can't have the same interface pointer for
+ any interface
+ */
+ if (pFirst == pSecond) {
+ return TRUE;
+ }
+ /* OK - do it the hard way - check if they have the same
+ IUnknown pointers - a single object can only have one of these
+ */
+ LPUNKNOWN pUnknown1; // Retrieve the IUnknown interface
+ LPUNKNOWN pUnknown2; // Retrieve the other IUnknown interface
+ HRESULT hr; // General OLE return code
+
+ ASSERT(pFirst);
+ ASSERT(pSecond);
+
+ /* See if the IUnknown pointers match */
+
+ hr = pFirst->QueryInterface(IID_IUnknown,(void **) &pUnknown1);
+ if (FAILED(hr)) {
+ return FALSE;
+ }
+ ASSERT(pUnknown1);
+
+ /* Release the extra interface we hold */
+
+ pUnknown1->Release();
+
+ hr = pSecond->QueryInterface(IID_IUnknown,(void **) &pUnknown2);
+ if (FAILED(hr)) {
+ return FALSE;
+ }
+ ASSERT(pUnknown2);
+
+ /* Release the extra interface we hold */
+
+ pUnknown2->Release();
+ return (pUnknown1 == pUnknown2);
+}
+
diff --git a/dshow_base/combase.h b/dshow_base/combase.h
index 901056b..f735ba9 100644
--- a/dshow_base/combase.h
+++ b/dshow_base/combase.h
@@ -1,319 +1,305 @@
-//------------------------------------------------------------------------------
-// File: ComBase.h
-//
-// Desc: DirectShow base classes - defines a class hierarchy for creating
-// COM objects.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-/*
-
-a. Derive your COM object from CUnknown
-
-b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT *
- and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls
- to. The HRESULT * allows error codes to be passed around constructors and
- the TCHAR * is a descriptive name that can be printed on the debugger.
-
- It is important that constructors only change the HRESULT * if they have
- to set an ERROR code, if it was successful then leave it alone or you may
- overwrite an error code from an object previously created.
-
- When you call a constructor the descriptive name should be in static store
- as we do not copy the string. To stop large amounts of memory being used
- in retail builds by all these static strings use the NAME macro,
-
- CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr);
- if (FAILED(hr)) {
- return hr;
- }
-
- In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class
- knows not to do anything with objects that don't have a name.
-
-c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and
- TCHAR * to the CUnknown constructor. You can set the HRESULT if you have an
- error, or just simply pass it through to the constructor.
-
- The object creation will fail in the class factory if the HRESULT indicates
- an error (ie FAILED(HRESULT) == TRUE)
-
-d. Create a FactoryTemplate with your object's class id and CreateInstance
- function.
-
-Then (for each interface) either
-
-Multiple inheritance
-
-1. Also derive it from ISomeInterface
-2. Include DECLARE_IUNKNOWN in your class definition to declare
- implementations of QueryInterface, AddRef and Release that
- call the outer unknown
-3. Override NonDelegatingQueryInterface to expose ISomeInterface by
- code something like
-
- if (riid == IID_ISomeInterface) {
- return GetInterface((ISomeInterface *) this, ppv);
- } else {
- return CUnknown::NonDelegatingQueryInterface(riid, ppv);
- }
-
-4. Declare and implement the member functions of ISomeInterface.
-
-or: Nested interfaces
-
-1. Declare a class derived from CUnknown
-2. Include DECLARE_IUNKNOWN in your class definition
-3. Override NonDelegatingQueryInterface to expose ISomeInterface by
- code something like
-
- if (riid == IID_ISomeInterface) {
- return GetInterface((ISomeInterface *) this, ppv);
- } else {
- return CUnknown::NonDelegatingQueryInterface(riid, ppv);
- }
-
-4. Implement the member functions of ISomeInterface. Use GetOwner() to
- access the COM object class.
-
-And in your COM object class:
-
-5. Make the nested class a friend of the COM object class, and declare
- an instance of the nested class as a member of the COM object class.
-
- NOTE that because you must always pass the outer unknown and an hResult
- to the CUnknown constructor you cannot use a default constructor, in
- other words you will have to make the member variable a pointer to the
- class and make a NEW call in your constructor to actually create it.
-
-6. override the NonDelegatingQueryInterface with code like this:
-
- if (riid == IID_ISomeInterface) {
- return m_pImplFilter->
- NonDelegatingQueryInterface(IID_ISomeInterface, ppv);
- } else {
- return CUnknown::NonDelegatingQueryInterface(riid, ppv);
- }
-
-You can have mixed classes which support some interfaces via multiple
-inheritance and some via nested classes
-
-*/
-
-#ifndef __COMBASE__
-#define __COMBASE__
-
-// Filter Setup data structures no defined in axextend.idl
-
-typedef REGPINTYPES
-AMOVIESETUP_MEDIATYPE, * PAMOVIESETUP_MEDIATYPE, * FAR LPAMOVIESETUP_MEDIATYPE;
-
-typedef REGFILTERPINS
-AMOVIESETUP_PIN, * PAMOVIESETUP_PIN, * FAR LPAMOVIESETUP_PIN;
-
-typedef struct _AMOVIESETUP_FILTER
-{
- const CLSID * clsID;
- const WCHAR * strName;
- DWORD dwMerit;
- UINT nPins;
- const AMOVIESETUP_PIN * lpPin;
-}
-AMOVIESETUP_FILTER, * PAMOVIESETUP_FILTER, * FAR LPAMOVIESETUP_FILTER;
-
-/* The DLLENTRY module initialises the module handle on loading */
-
-extern HINSTANCE g_hInst;
-
-/* On DLL load remember which platform we are running on */
-
-extern DWORD g_amPlatform;
-extern OSVERSIONINFO g_osInfo; // Filled in by GetVersionEx
-
-/* Version of IUnknown that is renamed to allow a class to support both
- non delegating and delegating IUnknowns in the same COM object */
-
-#ifndef INONDELEGATINGUNKNOWN_DEFINED
-DECLARE_INTERFACE(INonDelegatingUnknown)
-{
- STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE;
- STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE;
-};
-#define INONDELEGATINGUNKNOWN_DEFINED
-#endif
-
-typedef INonDelegatingUnknown *PNDUNKNOWN;
-
-
-/* This is the base object class that supports active object counting. As
- part of the debug facilities we trace every time a C++ object is created
- or destroyed. The name of the object has to be passed up through the class
- derivation list during construction as you cannot call virtual functions
- in the constructor. The downside of all this is that every single object
- constructor has to take an object name parameter that describes it */
-
-class CBaseObject
-{
-
-private:
-
- // Disable the copy constructor and assignment by default so you will get
- // compiler errors instead of unexpected behaviour if you pass objects
- // by value or assign objects.
- CBaseObject(const CBaseObject& objectSrc); // no implementation
- void operator=(const CBaseObject& objectSrc); // no implementation
-
-private:
- static LONG m_cObjects; /* Total number of objects active */
-
-protected:
-#ifdef DEBUG
- DWORD m_dwCookie; /* Cookie identifying this object */
-#endif
-
-
-public:
-
- /* These increment and decrement the number of active objects */
-
- CBaseObject(const TCHAR *pName);
-#ifdef UNICODE
- CBaseObject(const char *pName);
-#endif
- ~CBaseObject();
-
- /* Call this to find if there are any CUnknown derived objects active */
-
- static LONG ObjectsActive() {
- return m_cObjects;
- };
-};
-
-
-/* An object that supports one or more COM interfaces will be based on
- this class. It supports counting of total objects for DLLCanUnloadNow
- support, and an implementation of the core non delegating IUnknown */
-
-class AM_NOVTABLE CUnknown : public INonDelegatingUnknown,
- public CBaseObject
-{
-private:
- const LPUNKNOWN m_pUnknown; /* Owner of this object */
-
-protected: /* So we can override NonDelegatingRelease() */
- volatile LONG m_cRef; /* Number of reference counts */
-
-public:
-
- CUnknown(const TCHAR *pName, LPUNKNOWN pUnk);
- virtual ~CUnknown() {};
-
- // This is redundant, just use the other constructor
- // as we never touch the HRESULT in this anyway
- CUnknown(TCHAR *pName, LPUNKNOWN pUnk,HRESULT *phr);
-#ifdef UNICODE
- CUnknown(const char *pName, LPUNKNOWN pUnk);
- CUnknown(char *pName, LPUNKNOWN pUnk,HRESULT *phr);
-#endif
-
- /* Return the owner of this object */
-
- LPUNKNOWN GetOwner() const {
- return m_pUnknown;
- };
-
- /* Called from the class factory to create a new instance, it is
- pure virtual so it must be overriden in your derived class */
-
- /* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */
-
- /* Non delegating unknown implementation */
-
- STDMETHODIMP NonDelegatingQueryInterface(REFIID, void **);
- STDMETHODIMP_(ULONG) NonDelegatingAddRef();
- STDMETHODIMP_(ULONG) NonDelegatingRelease();
-};
-
-#if (_MSC_VER <= 1200)
-#pragma warning(disable:4211)
-
-/* The standard InterlockedXXX functions won't take volatiles */
-static inline LONG WINAPI InterlockedIncrement( volatile LONG * plong )
-{ return InterlockedIncrement( const_cast( plong ) ); }
-
-static inline LONG WINAPI InterlockedDecrement( volatile LONG * plong )
-{ return InterlockedDecrement( const_cast( plong ) ); }
-
-#pragma warning(default:4211)
-#endif
-
-
-/* Return an interface pointer to a requesting client
- performing a thread safe AddRef as necessary */
-
-STDAPI GetInterface(LPUNKNOWN pUnk, void **ppv);
-
-/* A function that can create a new COM object */
-
-typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(LPUNKNOWN pUnkOuter, HRESULT *phr);
-
-/* A function (can be NULL) which is called from the DLL entrypoint
- routine for each factory template:
-
- bLoading - TRUE on DLL load, FALSE on DLL unload
- rclsid - the m_ClsID of the entry
-*/
-typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);
-
-/* Create one of these per object class in an array so that
- the default class factory code can create new instances */
-
-class CFactoryTemplate {
-
-public:
-
- const WCHAR * m_Name;
- const CLSID * m_ClsID;
- LPFNNewCOMObject m_lpfnNew;
- LPFNInitRoutine m_lpfnInit;
- const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter;
-
- BOOL IsClassID(REFCLSID rclsid) const {
- return (IsEqualCLSID(*m_ClsID,rclsid));
- };
-
- CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr) const {
- CheckPointer(phr,NULL);
- return m_lpfnNew(pUnk, phr);
- };
-};
-
-
-/* You must override the (pure virtual) NonDelegatingQueryInterface to return
- interface pointers (using GetInterface) to the interfaces your derived
- class supports (the default implementation only supports IUnknown) */
-
-#define DECLARE_IUNKNOWN \
- STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { \
- return GetOwner()->QueryInterface(riid,ppv); \
- }; \
- STDMETHODIMP_(ULONG) AddRef() { \
- return GetOwner()->AddRef(); \
- }; \
- STDMETHODIMP_(ULONG) Release() { \
- return GetOwner()->Release(); \
- };
-
-
-
-HINSTANCE LoadOLEAut32();
-
-
-#endif /* __COMBASE__ */
-
-
-
-
+//------------------------------------------------------------------------------
+// File: ComBase.h
+//
+// Desc: DirectShow base classes - defines a class hierarchy for creating
+// COM objects.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+/*
+
+a. Derive your COM object from CUnknown
+
+b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT *
+ and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls
+ to. The HRESULT * allows error codes to be passed around constructors and
+ the TCHAR * is a descriptive name that can be printed on the debugger.
+
+ It is important that constructors only change the HRESULT * if they have
+ to set an ERROR code, if it was successful then leave it alone or you may
+ overwrite an error code from an object previously created.
+
+ When you call a constructor the descriptive name should be in static store
+ as we do not copy the string. To stop large amounts of memory being used
+ in retail builds by all these static strings use the NAME macro,
+
+ CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class
+ knows not to do anything with objects that don't have a name.
+
+c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and
+ TCHAR * to the CUnknown constructor. You can set the HRESULT if you have an
+ error, or just simply pass it through to the constructor.
+
+ The object creation will fail in the class factory if the HRESULT indicates
+ an error (ie FAILED(HRESULT) == TRUE)
+
+d. Create a FactoryTemplate with your object's class id and CreateInstance
+ function.
+
+Then (for each interface) either
+
+Multiple inheritance
+
+1. Also derive it from ISomeInterface
+2. Include DECLARE_IUNKNOWN in your class definition to declare
+ implementations of QueryInterface, AddRef and Release that
+ call the outer unknown
+3. Override NonDelegatingQueryInterface to expose ISomeInterface by
+ code something like
+
+ if (riid == IID_ISomeInterface) {
+ return GetInterface((ISomeInterface *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+
+4. Declare and implement the member functions of ISomeInterface.
+
+or: Nested interfaces
+
+1. Declare a class derived from CUnknown
+2. Include DECLARE_IUNKNOWN in your class definition
+3. Override NonDelegatingQueryInterface to expose ISomeInterface by
+ code something like
+
+ if (riid == IID_ISomeInterface) {
+ return GetInterface((ISomeInterface *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+
+4. Implement the member functions of ISomeInterface. Use GetOwner() to
+ access the COM object class.
+
+And in your COM object class:
+
+5. Make the nested class a friend of the COM object class, and declare
+ an instance of the nested class as a member of the COM object class.
+
+ NOTE that because you must always pass the outer unknown and an hResult
+ to the CUnknown constructor you cannot use a default constructor, in
+ other words you will have to make the member variable a pointer to the
+ class and make a NEW call in your constructor to actually create it.
+
+6. override the NonDelegatingQueryInterface with code like this:
+
+ if (riid == IID_ISomeInterface) {
+ return m_pImplFilter->
+ NonDelegatingQueryInterface(IID_ISomeInterface, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+
+You can have mixed classes which support some interfaces via multiple
+inheritance and some via nested classes
+
+*/
+
+#ifndef __COMBASE__
+#define __COMBASE__
+
+// Filter Setup data structures no defined in axextend.idl
+
+typedef REGPINTYPES
+AMOVIESETUP_MEDIATYPE, * PAMOVIESETUP_MEDIATYPE, * FAR LPAMOVIESETUP_MEDIATYPE;
+
+typedef REGFILTERPINS
+AMOVIESETUP_PIN, * PAMOVIESETUP_PIN, * FAR LPAMOVIESETUP_PIN;
+
+typedef struct _AMOVIESETUP_FILTER
+{
+ const CLSID * clsID;
+ const WCHAR * strName;
+ DWORD dwMerit;
+ UINT nPins;
+ const AMOVIESETUP_PIN * lpPin;
+}
+AMOVIESETUP_FILTER, * PAMOVIESETUP_FILTER, * FAR LPAMOVIESETUP_FILTER;
+
+/* The DLLENTRY module initialises the module handle on loading */
+
+extern HINSTANCE g_hInst;
+
+/* On DLL load remember which platform we are running on */
+
+extern DWORD g_amPlatform;
+extern OSVERSIONINFO g_osInfo; // Filled in by GetVersionEx
+
+/* Version of IUnknown that is renamed to allow a class to support both
+ non delegating and delegating IUnknowns in the same COM object */
+
+#ifndef INONDELEGATINGUNKNOWN_DEFINED
+DECLARE_INTERFACE(INonDelegatingUnknown)
+{
+ STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE;
+ STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE;
+ STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE;
+};
+#define INONDELEGATINGUNKNOWN_DEFINED
+#endif
+
+typedef INonDelegatingUnknown *PNDUNKNOWN;
+
+
+/* This is the base object class that supports active object counting. As
+ part of the debug facilities we trace every time a C++ object is created
+ or destroyed. The name of the object has to be passed up through the class
+ derivation list during construction as you cannot call virtual functions
+ in the constructor. The downside of all this is that every single object
+ constructor has to take an object name parameter that describes it */
+
+class CBaseObject
+{
+
+private:
+
+ // Disable the copy constructor and assignment by default so you will get
+ // compiler errors instead of unexpected behaviour if you pass objects
+ // by value or assign objects.
+ CBaseObject(const CBaseObject& objectSrc); // no implementation
+ void operator=(const CBaseObject& objectSrc); // no implementation
+
+private:
+ static LONG m_cObjects; /* Total number of objects active */
+
+protected:
+#ifdef DEBUG
+ DWORD m_dwCookie; /* Cookie identifying this object */
+#endif
+
+
+public:
+
+ /* These increment and decrement the number of active objects */
+
+ CBaseObject(__in_opt LPCTSTR pName);
+#ifdef UNICODE
+ CBaseObject(__in_opt LPCSTR pName);
+#endif
+ ~CBaseObject();
+
+ /* Call this to find if there are any CUnknown derived objects active */
+
+ static LONG ObjectsActive() {
+ return m_cObjects;
+ };
+};
+
+
+/* An object that supports one or more COM interfaces will be based on
+ this class. It supports counting of total objects for DLLCanUnloadNow
+ support, and an implementation of the core non delegating IUnknown */
+
+class AM_NOVTABLE CUnknown : public INonDelegatingUnknown,
+ public CBaseObject
+{
+private:
+ const LPUNKNOWN m_pUnknown; /* Owner of this object */
+
+protected: /* So we can override NonDelegatingRelease() */
+ volatile LONG m_cRef; /* Number of reference counts */
+
+public:
+
+ CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk);
+ virtual ~CUnknown() {};
+
+ // This is redundant, just use the other constructor
+ // as we never touch the HRESULT in this anyway
+ CUnknown(__in_opt LPCTSTR Name, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr);
+#ifdef UNICODE
+ CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk);
+ CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk,__inout_opt HRESULT *phr);
+#endif
+
+ /* Return the owner of this object */
+
+ LPUNKNOWN GetOwner() const {
+ return m_pUnknown;
+ };
+
+ /* Called from the class factory to create a new instance, it is
+ pure virtual so it must be overriden in your derived class */
+
+ /* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */
+
+ /* Non delegating unknown implementation */
+
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **);
+ STDMETHODIMP_(ULONG) NonDelegatingAddRef();
+ STDMETHODIMP_(ULONG) NonDelegatingRelease();
+};
+
+/* Return an interface pointer to a requesting client
+ performing a thread safe AddRef as necessary */
+
+STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv);
+
+/* A function that can create a new COM object */
+
+typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(__in_opt LPUNKNOWN pUnkOuter, __inout_opt HRESULT *phr);
+
+/* A function (can be NULL) which is called from the DLL entrypoint
+ routine for each factory template:
+
+ bLoading - TRUE on DLL load, FALSE on DLL unload
+ rclsid - the m_ClsID of the entry
+*/
+typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);
+
+/* Create one of these per object class in an array so that
+ the default class factory code can create new instances */
+
+class CFactoryTemplate {
+
+public:
+
+ const WCHAR * m_Name;
+ const CLSID * m_ClsID;
+ LPFNNewCOMObject m_lpfnNew;
+ LPFNInitRoutine m_lpfnInit;
+ const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter;
+
+ BOOL IsClassID(REFCLSID rclsid) const {
+ return (IsEqualCLSID(*m_ClsID,rclsid));
+ };
+
+ CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) const {
+ CheckPointer(phr,NULL);
+ return m_lpfnNew(pUnk, phr);
+ };
+};
+
+
+/* You must override the (pure virtual) NonDelegatingQueryInterface to return
+ interface pointers (using GetInterface) to the interfaces your derived
+ class supports (the default implementation only supports IUnknown) */
+
+#define DECLARE_IUNKNOWN \
+ STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv) { \
+ return GetOwner()->QueryInterface(riid,ppv); \
+ }; \
+ STDMETHODIMP_(ULONG) AddRef() { \
+ return GetOwner()->AddRef(); \
+ }; \
+ STDMETHODIMP_(ULONG) Release() { \
+ return GetOwner()->Release(); \
+ };
+
+
+
+HINSTANCE LoadOLEAut32();
+
+
+#endif /* __COMBASE__ */
+
+
+
+
diff --git a/dshow_base/cprop.cpp b/dshow_base/cprop.cpp
new file mode 100644
index 0000000..7bd76b4
--- /dev/null
+++ b/dshow_base/cprop.cpp
@@ -0,0 +1,383 @@
+//------------------------------------------------------------------------------
+// File: CProp.cpp
+//
+// Desc: DirectShow base classes - implements CBasePropertyPage class.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+
+// Constructor for the base property page class. As described in the header
+// file we must be initialised with dialog and title resource identifiers.
+// The class supports IPropertyPage and overrides AddRef and Release calls
+// to keep track of the reference counts. When the last count is released
+// we call SetPageSite(NULL) and SetObjects(0,NULL) to release interfaces
+// previously obtained by the property page when it had SetObjects called
+
+CBasePropertyPage::CBasePropertyPage(__in_opt LPCTSTR pName, // Debug only name
+ __inout_opt LPUNKNOWN pUnk, // COM Delegator
+ int DialogId, // Resource ID
+ int TitleId) : // To get tital
+ CUnknown(pName,pUnk),
+ m_DialogId(DialogId),
+ m_TitleId(TitleId),
+ m_hwnd(NULL),
+ m_Dlg(NULL),
+ m_pPageSite(NULL),
+ m_bObjectSet(FALSE),
+ m_bDirty(FALSE)
+{
+}
+
+#ifdef UNICODE
+CBasePropertyPage::CBasePropertyPage(__in_opt LPCSTR pName, // Debug only name
+ __inout_opt LPUNKNOWN pUnk, // COM Delegator
+ int DialogId, // Resource ID
+ int TitleId) : // To get tital
+ CUnknown(pName,pUnk),
+ m_DialogId(DialogId),
+ m_TitleId(TitleId),
+ m_hwnd(NULL),
+ m_Dlg(NULL),
+ m_pPageSite(NULL),
+ m_bObjectSet(FALSE),
+ m_bDirty(FALSE)
+{
+}
+#endif
+
+// Increment our reference count
+
+STDMETHODIMP_(ULONG) CBasePropertyPage::NonDelegatingAddRef()
+{
+ LONG lRef = InterlockedIncrement(&m_cRef);
+ ASSERT(lRef > 0);
+ return max(ULONG(m_cRef),1ul);
+}
+
+
+// Release a reference count and protect against reentrancy
+
+STDMETHODIMP_(ULONG) CBasePropertyPage::NonDelegatingRelease()
+{
+ // If the reference count drops to zero delete ourselves
+
+ LONG lRef = InterlockedDecrement(&m_cRef);
+ if (lRef == 0) {
+ m_cRef++;
+ SetPageSite(NULL);
+ SetObjects(0,NULL);
+ delete this;
+ return ULONG(0);
+ } else {
+ // Don't touch m_cRef again here!
+ return max(ULONG(lRef),1ul);
+ }
+}
+
+
+// Expose our IPropertyPage interface
+
+STDMETHODIMP
+CBasePropertyPage::NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv)
+{
+ if (riid == IID_IPropertyPage) {
+ return GetInterface((IPropertyPage *)this,ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid,ppv);
+ }
+}
+
+
+// Get the page info so that the page site can size itself
+
+STDMETHODIMP CBasePropertyPage::GetPageInfo(__out LPPROPPAGEINFO pPageInfo)
+{
+ CheckPointer(pPageInfo,E_POINTER);
+ WCHAR wszTitle[STR_MAX_LENGTH];
+ WideStringFromResource(wszTitle,m_TitleId);
+
+ // Allocate dynamic memory for the property page title
+
+ LPOLESTR pszTitle;
+ HRESULT hr = AMGetWideString(wszTitle, &pszTitle);
+ if (FAILED(hr)) {
+ NOTE("No caption memory");
+ return hr;
+ }
+
+ pPageInfo->cb = sizeof(PROPPAGEINFO);
+ pPageInfo->pszTitle = pszTitle;
+ pPageInfo->pszDocString = NULL;
+ pPageInfo->pszHelpFile = NULL;
+ pPageInfo->dwHelpContext = 0;
+
+ // Set defaults in case GetDialogSize fails
+ pPageInfo->size.cx = 340;
+ pPageInfo->size.cy = 150;
+
+ GetDialogSize(m_DialogId, DialogProc,0L,&pPageInfo->size);
+ return NOERROR;
+}
+
+
+// Handles the messages for our property window
+
+INT_PTR CALLBACK CBasePropertyPage::DialogProc(HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ CBasePropertyPage *pPropertyPage;
+
+ switch (uMsg) {
+
+ case WM_INITDIALOG:
+
+ _SetWindowLongPtr(hwnd, DWLP_USER, lParam);
+
+ // This pointer may be NULL when calculating size
+
+ pPropertyPage = (CBasePropertyPage *) lParam;
+ if (pPropertyPage == NULL) {
+ return (LRESULT) 1;
+ }
+ pPropertyPage->m_Dlg = hwnd;
+ }
+
+ // This pointer may be NULL when calculating size
+
+ pPropertyPage = _GetWindowLongPtr(hwnd, DWLP_USER);
+ if (pPropertyPage == NULL) {
+ return (LRESULT) 1;
+ }
+ return pPropertyPage->OnReceiveMessage(hwnd,uMsg,wParam,lParam);
+}
+
+
+// Tells us the object that should be informed of the property changes
+
+STDMETHODIMP CBasePropertyPage::SetObjects(ULONG cObjects,__in_ecount_opt(cObjects) LPUNKNOWN *ppUnk)
+{
+ if (cObjects == 1) {
+
+ if ((ppUnk == NULL) || (*ppUnk == NULL)) {
+ return E_POINTER;
+ }
+
+ // Set a flag to say that we have set the Object
+ m_bObjectSet = TRUE ;
+ return OnConnect(*ppUnk);
+
+ } else if (cObjects == 0) {
+
+ // Set a flag to say that we have not set the Object for the page
+ m_bObjectSet = FALSE ;
+ return OnDisconnect();
+ }
+
+ DbgBreak("No support for more than one object");
+ return E_UNEXPECTED;
+}
+
+
+// Create the window we will use to edit properties
+
+STDMETHODIMP CBasePropertyPage::Activate(HWND hwndParent,
+ LPCRECT pRect,
+ BOOL fModal)
+{
+ CheckPointer(pRect,E_POINTER);
+
+ // Return failure if SetObject has not been called.
+ if (m_bObjectSet == FALSE) {
+ return E_UNEXPECTED;
+ }
+
+ if (m_hwnd) {
+ return E_UNEXPECTED;
+ }
+
+ m_hwnd = CreateDialogParam(g_hInst,
+ MAKEINTRESOURCE(m_DialogId),
+ hwndParent,
+ DialogProc,
+ (LPARAM) this);
+ if (m_hwnd == NULL) {
+ return E_OUTOFMEMORY;
+ }
+
+ OnActivate();
+ Move(pRect);
+ return Show(SW_SHOWNORMAL);
+}
+
+
+// Set the position of the property page
+
+STDMETHODIMP CBasePropertyPage::Move(LPCRECT pRect)
+{
+ CheckPointer(pRect,E_POINTER);
+
+ if (m_hwnd == NULL) {
+ return E_UNEXPECTED;
+ }
+
+ MoveWindow(m_hwnd, // Property page handle
+ pRect->left, // x coordinate
+ pRect->top, // y coordinate
+ WIDTH(pRect), // Overall window width
+ HEIGHT(pRect), // And likewise height
+ TRUE); // Should we repaint it
+
+ return NOERROR;
+}
+
+
+// Display the property dialog
+
+STDMETHODIMP CBasePropertyPage::Show(UINT nCmdShow)
+{
+ // Have we been activated yet
+
+ if (m_hwnd == NULL) {
+ return E_UNEXPECTED;
+ }
+
+ // Ignore wrong show flags
+
+ if ((nCmdShow != SW_SHOW) && (nCmdShow != SW_SHOWNORMAL) && (nCmdShow != SW_HIDE)) {
+ return E_INVALIDARG;
+ }
+
+ ShowWindow(m_hwnd,nCmdShow);
+ InvalidateRect(m_hwnd,NULL,TRUE);
+ return NOERROR;
+}
+
+
+// Destroy the property page dialog
+
+STDMETHODIMP CBasePropertyPage::Deactivate(void)
+{
+ if (m_hwnd == NULL) {
+ return E_UNEXPECTED;
+ }
+
+ // Remove WS_EX_CONTROLPARENT before DestroyWindow call
+
+ DWORD dwStyle = GetWindowLong(m_hwnd, GWL_EXSTYLE);
+ dwStyle = dwStyle & (~WS_EX_CONTROLPARENT);
+
+ // Set m_hwnd to be NULL temporarily so the message handler
+ // for WM_STYLECHANGING doesn't add the WS_EX_CONTROLPARENT
+ // style back in
+ HWND hwnd = m_hwnd;
+ m_hwnd = NULL;
+ SetWindowLong(hwnd, GWL_EXSTYLE, dwStyle);
+ m_hwnd = hwnd;
+
+ OnDeactivate();
+
+ // Destroy the dialog window
+
+ DestroyWindow(m_hwnd);
+ m_hwnd = NULL;
+ return NOERROR;
+}
+
+
+// Tells the application property page site
+
+STDMETHODIMP CBasePropertyPage::SetPageSite(__in_opt LPPROPERTYPAGESITE pPageSite)
+{
+ if (pPageSite) {
+
+ if (m_pPageSite) {
+ return E_UNEXPECTED;
+ }
+
+ m_pPageSite = pPageSite;
+ m_pPageSite->AddRef();
+
+ } else {
+
+ if (m_pPageSite == NULL) {
+ return E_UNEXPECTED;
+ }
+
+ m_pPageSite->Release();
+ m_pPageSite = NULL;
+ }
+ return NOERROR;
+}
+
+
+// Apply any changes so far made
+
+STDMETHODIMP CBasePropertyPage::Apply()
+{
+ // In ActiveMovie 1.0 we used to check whether we had been activated or
+ // not. This is too constrictive. Apply should be allowed as long as
+ // SetObject was called to set an object. So we will no longer check to
+ // see if we have been activated (ie., m_hWnd != NULL), but instead
+ // make sure that m_bObjectSet is TRUE (ie., SetObject has been called).
+
+ if (m_bObjectSet == FALSE) {
+ return E_UNEXPECTED;
+ }
+
+ // Must have had a site set
+
+ if (m_pPageSite == NULL) {
+ return E_UNEXPECTED;
+ }
+
+ // Has anything changed
+
+ if (m_bDirty == FALSE) {
+ return NOERROR;
+ }
+
+ // Commit derived class changes
+
+ HRESULT hr = OnApplyChanges();
+ if (SUCCEEDED(hr)) {
+ m_bDirty = FALSE;
+ }
+ return hr;
+}
+
+
+// Base class definition for message handling
+
+INT_PTR CBasePropertyPage::OnReceiveMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
+{
+ // we would like the TAB key to move around the tab stops in our property
+ // page, but for some reason OleCreatePropertyFrame clears the CONTROLPARENT
+ // style behind our back, so we need to switch it back on now behind its
+ // back. Otherwise the tab key will be useless in every page.
+ //
+
+ CBasePropertyPage *pPropertyPage;
+ {
+ pPropertyPage = _GetWindowLongPtr(hwnd, DWLP_USER);
+
+ if (pPropertyPage->m_hwnd == NULL) {
+ return 0;
+ }
+ switch (uMsg) {
+ case WM_STYLECHANGING:
+ if (wParam == GWL_EXSTYLE) {
+ LPSTYLESTRUCT lpss = (LPSTYLESTRUCT)lParam;
+ lpss->styleNew |= WS_EX_CONTROLPARENT;
+ return 0;
+ }
+ }
+ }
+
+ return DefWindowProc(hwnd,uMsg,wParam,lParam);
+}
+
diff --git a/dshow_base/cprop.h b/dshow_base/cprop.h
index e254eaa..db44940 100644
--- a/dshow_base/cprop.h
+++ b/dshow_base/cprop.h
@@ -1,95 +1,95 @@
-//------------------------------------------------------------------------------
-// File: CProp.h
-//
-// Desc: DirectShow base classes.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __CPROP__
-#define __CPROP__
-
-// Base property page class. Filters typically expose custom properties by
-// implementing special control interfaces, examples are IDirectDrawVideo
-// and IQualProp on renderers. This allows property pages to be built that
-// use the given interface. Applications such as the ActiveMovie OCX query
-// filters for the property pages they support and expose them to the user
-//
-// This class provides all the framework for a property page. A property
-// page is a COM object that supports IPropertyPage. We should be created
-// with a resource ID for the dialog which we will load when required. We
-// should also be given in the constructor a resource ID for a title string
-// we will load from the DLLs STRINGTABLE. The property page titles must be
-// stored in resource files so that they can be easily internationalised
-//
-// We have a number of virtual methods (not PURE) that may be overriden in
-// derived classes to query for interfaces and so on. These functions have
-// simple implementations here that just return NOERROR. Derived classes
-// will almost definately have to override the message handler method called
-// OnReceiveMessage. We have a static dialog procedure that calls the method
-// so that derived classes don't have to fiddle around with the this pointer
-
-class AM_NOVTABLE CBasePropertyPage : public IPropertyPage, public CUnknown
-{
-protected:
-
- LPPROPERTYPAGESITE m_pPageSite; // Details for our property site
- HWND m_hwnd; // Window handle for the page
- HWND m_Dlg; // Actual dialog window handle
- BOOL m_bDirty; // Has anything been changed
- int m_TitleId; // Resource identifier for title
- int m_DialogId; // Dialog resource identifier
-
- static INT_PTR CALLBACK DialogProc(HWND hwnd,
- UINT uMsg,
- WPARAM wParam,
- LPARAM lParam);
-
-private:
- BOOL m_bObjectSet ; // SetObject has been called or not.
-public:
-
- CBasePropertyPage(TCHAR *pName, // Debug only name
- LPUNKNOWN pUnk, // COM Delegator
- int DialogId, // Resource ID
- int TitleId); // To get tital
-
-#ifdef UNICODE
- CBasePropertyPage(CHAR *pName,
- LPUNKNOWN pUnk,
- int DialogId,
- int TitleId);
-#endif
- virtual ~CBasePropertyPage() { };
- DECLARE_IUNKNOWN
-
- // Override these virtual methods
-
- virtual HRESULT OnConnect(IUnknown *pUnknown) { return NOERROR; };
- virtual HRESULT OnDisconnect() { return NOERROR; };
- virtual HRESULT OnActivate() { return NOERROR; };
- virtual HRESULT OnDeactivate() { return NOERROR; };
- virtual HRESULT OnApplyChanges() { return NOERROR; };
- virtual INT_PTR OnReceiveMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
-
- // These implement an IPropertyPage interface
-
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv);
- STDMETHODIMP_(ULONG) NonDelegatingRelease();
- STDMETHODIMP_(ULONG) NonDelegatingAddRef();
- STDMETHODIMP SetPageSite(LPPROPERTYPAGESITE pPageSite);
- STDMETHODIMP Activate(HWND hwndParent,LPCRECT prect,BOOL fModal);
- STDMETHODIMP Deactivate(void);
- STDMETHODIMP GetPageInfo(LPPROPPAGEINFO pPageInfo);
- STDMETHODIMP SetObjects(ULONG cObjects, LPUNKNOWN *ppUnk);
- STDMETHODIMP Show(UINT nCmdShow);
- STDMETHODIMP Move(LPCRECT prect);
- STDMETHODIMP IsPageDirty(void) { return m_bDirty ? S_OK : S_FALSE; }
- STDMETHODIMP Apply(void);
- STDMETHODIMP Help(LPCWSTR lpszHelpDir) { return E_NOTIMPL; }
- STDMETHODIMP TranslateAccelerator(LPMSG lpMsg) { return E_NOTIMPL; }
-};
-
-#endif // __CPROP__
-
+//------------------------------------------------------------------------------
+// File: CProp.h
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __CPROP__
+#define __CPROP__
+
+// Base property page class. Filters typically expose custom properties by
+// implementing special control interfaces, examples are IDirectDrawVideo
+// and IQualProp on renderers. This allows property pages to be built that
+// use the given interface. Applications such as the ActiveMovie OCX query
+// filters for the property pages they support and expose them to the user
+//
+// This class provides all the framework for a property page. A property
+// page is a COM object that supports IPropertyPage. We should be created
+// with a resource ID for the dialog which we will load when required. We
+// should also be given in the constructor a resource ID for a title string
+// we will load from the DLLs STRINGTABLE. The property page titles must be
+// stored in resource files so that they can be easily internationalised
+//
+// We have a number of virtual methods (not PURE) that may be overriden in
+// derived classes to query for interfaces and so on. These functions have
+// simple implementations here that just return NOERROR. Derived classes
+// will almost definately have to override the message handler method called
+// OnReceiveMessage. We have a static dialog procedure that calls the method
+// so that derived classes don't have to fiddle around with the this pointer
+
+class AM_NOVTABLE CBasePropertyPage : public IPropertyPage, public CUnknown
+{
+protected:
+
+ LPPROPERTYPAGESITE m_pPageSite; // Details for our property site
+ HWND m_hwnd; // Window handle for the page
+ HWND m_Dlg; // Actual dialog window handle
+ BOOL m_bDirty; // Has anything been changed
+ int m_TitleId; // Resource identifier for title
+ int m_DialogId; // Dialog resource identifier
+
+ static INT_PTR CALLBACK DialogProc(HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam);
+
+private:
+ BOOL m_bObjectSet ; // SetObject has been called or not.
+public:
+
+ CBasePropertyPage(__in_opt LPCTSTR pName, // Debug only name
+ __inout_opt LPUNKNOWN pUnk, // COM Delegator
+ int DialogId, // Resource ID
+ int TitleId); // To get tital
+
+#ifdef UNICODE
+ CBasePropertyPage(__in_opt LPCSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ int DialogId,
+ int TitleId);
+#endif
+ virtual ~CBasePropertyPage() { };
+ DECLARE_IUNKNOWN
+
+ // Override these virtual methods
+
+ virtual HRESULT OnConnect(IUnknown *pUnknown) { return NOERROR; };
+ virtual HRESULT OnDisconnect() { return NOERROR; };
+ virtual HRESULT OnActivate() { return NOERROR; };
+ virtual HRESULT OnDeactivate() { return NOERROR; };
+ virtual HRESULT OnApplyChanges() { return NOERROR; };
+ virtual INT_PTR OnReceiveMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
+
+ // These implement an IPropertyPage interface
+
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+ STDMETHODIMP_(ULONG) NonDelegatingRelease();
+ STDMETHODIMP_(ULONG) NonDelegatingAddRef();
+ STDMETHODIMP SetPageSite(__in_opt LPPROPERTYPAGESITE pPageSite);
+ STDMETHODIMP Activate(HWND hwndParent, LPCRECT prect,BOOL fModal);
+ STDMETHODIMP Deactivate(void);
+ STDMETHODIMP GetPageInfo(__out LPPROPPAGEINFO pPageInfo);
+ STDMETHODIMP SetObjects(ULONG cObjects, __in_ecount_opt(cObjects) LPUNKNOWN *ppUnk);
+ STDMETHODIMP Show(UINT nCmdShow);
+ STDMETHODIMP Move(LPCRECT prect);
+ STDMETHODIMP IsPageDirty(void) { return m_bDirty ? S_OK : S_FALSE; }
+ STDMETHODIMP Apply(void);
+ STDMETHODIMP Help(LPCWSTR lpszHelpDir) { return E_NOTIMPL; }
+ STDMETHODIMP TranslateAccelerator(__inout LPMSG lpMsg) { return E_NOTIMPL; }
+};
+
+#endif // __CPROP__
+
diff --git a/dshow_base/ctlutil.cpp b/dshow_base/ctlutil.cpp
new file mode 100644
index 0000000..8ccb9dc
--- /dev/null
+++ b/dshow_base/ctlutil.cpp
@@ -0,0 +1,2541 @@
+//------------------------------------------------------------------------------
+// File: CtlUtil.cpp
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+// Base classes implementing IDispatch parsing for the basic control dual
+// interfaces. Derive from these and implement just the custom method and
+// property methods. We also implement CPosPassThru that can be used by
+// renderers and transforms to pass by IMediaPosition and IMediaSeeking
+
+
+#include
+#include
+#include "seekpt.h"
+
+// 'bool' non standard reserved word
+#pragma warning(disable:4237)
+
+
+// --- CBaseDispatch implementation ----------
+CBaseDispatch::~CBaseDispatch()
+{
+ if (m_pti) {
+ m_pti->Release();
+ }
+}
+
+
+// return 1 if we support GetTypeInfo
+
+STDMETHODIMP
+CBaseDispatch::GetTypeInfoCount(__out UINT * pctinfo)
+{
+ CheckPointer(pctinfo,E_POINTER);
+ ValidateReadWritePtr(pctinfo,sizeof(UINT *));
+ *pctinfo = 1;
+ return S_OK;
+}
+
+
+typedef HRESULT (STDAPICALLTYPE *LPLOADTYPELIB)(
+ const OLECHAR FAR *szFile,
+ __deref_out ITypeLib FAR* FAR* pptlib);
+
+typedef HRESULT (STDAPICALLTYPE *LPLOADREGTYPELIB)(REFGUID rguid,
+ WORD wVerMajor,
+ WORD wVerMinor,
+ LCID lcid,
+ __deref_out ITypeLib FAR* FAR* pptlib);
+
+// attempt to find our type library
+
+STDMETHODIMP
+CBaseDispatch::GetTypeInfo(
+ REFIID riid,
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo)
+{
+ CheckPointer(pptinfo,E_POINTER);
+ ValidateReadWritePtr(pptinfo,sizeof(ITypeInfo *));
+ HRESULT hr;
+
+ *pptinfo = NULL;
+
+ // we only support one type element
+ if (0 != itinfo) {
+ return TYPE_E_ELEMENTNOTFOUND;
+ }
+
+ if (NULL == pptinfo) {
+ return E_POINTER;
+ }
+
+ // always look for neutral
+ if (NULL == m_pti) {
+
+ LPLOADTYPELIB lpfnLoadTypeLib;
+ LPLOADREGTYPELIB lpfnLoadRegTypeLib;
+ ITypeLib *ptlib;
+ HINSTANCE hInst;
+
+ static const char szTypeLib[] = "LoadTypeLib";
+ static const char szRegTypeLib[] = "LoadRegTypeLib";
+ static const WCHAR szControl[] = L"control.tlb";
+
+ //
+ // Try to get the Ole32Aut.dll module handle.
+ //
+
+ hInst = LoadOLEAut32();
+ if (hInst == NULL) {
+ DWORD dwError = GetLastError();
+ return AmHresultFromWin32(dwError);
+ }
+ lpfnLoadRegTypeLib = (LPLOADREGTYPELIB)GetProcAddress(hInst,
+ szRegTypeLib);
+ if (lpfnLoadRegTypeLib == NULL) {
+ DWORD dwError = GetLastError();
+ return AmHresultFromWin32(dwError);
+ }
+
+ hr = (*lpfnLoadRegTypeLib)(LIBID_QuartzTypeLib, 1, 0, // version 1.0
+ lcid, &ptlib);
+
+ if (FAILED(hr)) {
+
+ // attempt to load directly - this will fill the
+ // registry in if it finds it
+
+ lpfnLoadTypeLib = (LPLOADTYPELIB)GetProcAddress(hInst, szTypeLib);
+ if (lpfnLoadTypeLib == NULL) {
+ DWORD dwError = GetLastError();
+ return AmHresultFromWin32(dwError);
+ }
+
+ hr = (*lpfnLoadTypeLib)(szControl, &ptlib);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+
+ hr = ptlib->GetTypeInfoOfGuid(
+ riid,
+ &m_pti);
+
+ ptlib->Release();
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+
+ *pptinfo = m_pti;
+ m_pti->AddRef();
+ return S_OK;
+}
+
+
+STDMETHODIMP
+CBaseDispatch::GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid)
+{
+ // although the IDispatch riid is dead, we use this to pass from
+ // the interface implementation class to us the iid we are talking about.
+
+ ITypeInfo * pti;
+ HRESULT hr = GetTypeInfo(riid, 0, lcid, &pti);
+
+ if (SUCCEEDED(hr)) {
+ hr = pti->GetIDsOfNames(rgszNames, cNames, rgdispid);
+
+ pti->Release();
+ }
+ return hr;
+}
+
+
+// --- CMediaControl implementation ---------
+
+CMediaControl::CMediaControl(const TCHAR * name,LPUNKNOWN pUnk) :
+ CUnknown(name, pUnk)
+{
+}
+
+// expose our interfaces IMediaControl and IUnknown
+
+STDMETHODIMP
+CMediaControl::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ ValidateReadWritePtr(ppv,sizeof(PVOID));
+ if (riid == IID_IMediaControl) {
+ return GetInterface( (IMediaControl *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+// return 1 if we support GetTypeInfo
+
+STDMETHODIMP
+CMediaControl::GetTypeInfoCount(__out UINT * pctinfo)
+{
+ return m_basedisp.GetTypeInfoCount(pctinfo);
+}
+
+
+// attempt to find our type library
+
+STDMETHODIMP
+CMediaControl::GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo)
+{
+ return m_basedisp.GetTypeInfo(
+ IID_IMediaControl,
+ itinfo,
+ lcid,
+ pptinfo);
+}
+
+
+STDMETHODIMP
+CMediaControl::GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid)
+{
+ return m_basedisp.GetIDsOfNames(
+ IID_IMediaControl,
+ rgszNames,
+ cNames,
+ lcid,
+ rgdispid);
+}
+
+
+STDMETHODIMP
+CMediaControl::Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr)
+{
+ // this parameter is a dead leftover from an earlier interface
+ if (IID_NULL != riid) {
+ return DISP_E_UNKNOWNINTERFACE;
+ }
+
+ ITypeInfo * pti;
+ HRESULT hr = GetTypeInfo(0, lcid, &pti);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pti->Invoke(
+ (IMediaControl *)this,
+ dispidMember,
+ wFlags,
+ pdispparams,
+ pvarResult,
+ pexcepinfo,
+ puArgErr);
+
+ pti->Release();
+ return hr;
+}
+
+
+// --- CMediaEvent implementation ----------
+
+
+CMediaEvent::CMediaEvent(__in_opt LPCTSTR name,__in_opt LPUNKNOWN pUnk) :
+ CUnknown(name, pUnk)
+{
+}
+
+
+// expose our interfaces IMediaEvent and IUnknown
+
+STDMETHODIMP
+CMediaEvent::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ ValidateReadWritePtr(ppv,sizeof(PVOID));
+ if (riid == IID_IMediaEvent || riid == IID_IMediaEventEx) {
+ return GetInterface( (IMediaEventEx *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+// return 1 if we support GetTypeInfo
+
+STDMETHODIMP
+CMediaEvent::GetTypeInfoCount(__out UINT * pctinfo)
+{
+ return m_basedisp.GetTypeInfoCount(pctinfo);
+}
+
+
+// attempt to find our type library
+
+STDMETHODIMP
+CMediaEvent::GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo)
+{
+ return m_basedisp.GetTypeInfo(
+ IID_IMediaEvent,
+ itinfo,
+ lcid,
+ pptinfo);
+}
+
+
+STDMETHODIMP
+CMediaEvent::GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid)
+{
+ return m_basedisp.GetIDsOfNames(
+ IID_IMediaEvent,
+ rgszNames,
+ cNames,
+ lcid,
+ rgdispid);
+}
+
+
+STDMETHODIMP
+CMediaEvent::Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr)
+{
+ // this parameter is a dead leftover from an earlier interface
+ if (IID_NULL != riid) {
+ return DISP_E_UNKNOWNINTERFACE;
+ }
+
+ ITypeInfo * pti;
+ HRESULT hr = GetTypeInfo(0, lcid, &pti);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pti->Invoke(
+ (IMediaEvent *)this,
+ dispidMember,
+ wFlags,
+ pdispparams,
+ pvarResult,
+ pexcepinfo,
+ puArgErr);
+
+ pti->Release();
+ return hr;
+}
+
+
+// --- CMediaPosition implementation ----------
+
+
+CMediaPosition::CMediaPosition(__in_opt LPCTSTR name,__in_opt LPUNKNOWN pUnk) :
+ CUnknown(name, pUnk)
+{
+}
+
+CMediaPosition::CMediaPosition(__in_opt LPCTSTR name,
+ __in_opt LPUNKNOWN pUnk,
+ __inout HRESULT * phr) :
+ CUnknown(name, pUnk)
+{
+ UNREFERENCED_PARAMETER(phr);
+}
+
+
+// expose our interfaces IMediaPosition and IUnknown
+
+STDMETHODIMP
+CMediaPosition::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ ValidateReadWritePtr(ppv,sizeof(PVOID));
+ if (riid == IID_IMediaPosition) {
+ return GetInterface( (IMediaPosition *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+// return 1 if we support GetTypeInfo
+
+STDMETHODIMP
+CMediaPosition::GetTypeInfoCount(__out UINT * pctinfo)
+{
+ return m_basedisp.GetTypeInfoCount(pctinfo);
+}
+
+
+// attempt to find our type library
+
+STDMETHODIMP
+CMediaPosition::GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo)
+{
+ return m_basedisp.GetTypeInfo(
+ IID_IMediaPosition,
+ itinfo,
+ lcid,
+ pptinfo);
+}
+
+
+STDMETHODIMP
+CMediaPosition::GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid)
+{
+ return m_basedisp.GetIDsOfNames(
+ IID_IMediaPosition,
+ rgszNames,
+ cNames,
+ lcid,
+ rgdispid);
+}
+
+
+STDMETHODIMP
+CMediaPosition::Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr)
+{
+ // this parameter is a dead leftover from an earlier interface
+ if (IID_NULL != riid) {
+ return DISP_E_UNKNOWNINTERFACE;
+ }
+
+ ITypeInfo * pti;
+ HRESULT hr = GetTypeInfo(0, lcid, &pti);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pti->Invoke(
+ (IMediaPosition *)this,
+ dispidMember,
+ wFlags,
+ pdispparams,
+ pvarResult,
+ pexcepinfo,
+ puArgErr);
+
+ pti->Release();
+ return hr;
+}
+
+
+// --- IMediaPosition and IMediaSeeking pass through class ----------
+
+
+CPosPassThru::CPosPassThru(__in_opt LPCTSTR pName,
+ __in_opt LPUNKNOWN pUnk,
+ __inout HRESULT *phr,
+ IPin *pPin) :
+ CMediaPosition(pName,pUnk),
+ m_pPin(pPin)
+{
+ if (pPin == NULL) {
+ *phr = E_POINTER;
+ return;
+ }
+}
+
+
+// Expose our IMediaSeeking and IMediaPosition interfaces
+
+STDMETHODIMP
+CPosPassThru::NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv)
+{
+ CheckPointer(ppv,E_POINTER);
+ *ppv = NULL;
+
+ if (riid == IID_IMediaSeeking) {
+ return GetInterface( static_cast(this), ppv);
+ }
+ return CMediaPosition::NonDelegatingQueryInterface(riid,ppv);
+}
+
+
+// Return the IMediaPosition interface from our peer
+
+HRESULT
+CPosPassThru::GetPeer(IMediaPosition ** ppMP)
+{
+ *ppMP = NULL;
+
+ IPin *pConnected;
+ HRESULT hr = m_pPin->ConnectedTo(&pConnected);
+ if (FAILED(hr)) {
+ return E_NOTIMPL;
+ }
+ IMediaPosition * pMP;
+ hr = pConnected->QueryInterface(IID_IMediaPosition, (void **) &pMP);
+ pConnected->Release();
+ if (FAILED(hr)) {
+ return E_NOTIMPL;
+ }
+
+ *ppMP = pMP;
+ return S_OK;
+}
+
+
+// Return the IMediaSeeking interface from our peer
+
+HRESULT
+CPosPassThru::GetPeerSeeking(__deref_out IMediaSeeking ** ppMS)
+{
+ *ppMS = NULL;
+
+ IPin *pConnected;
+ HRESULT hr = m_pPin->ConnectedTo(&pConnected);
+ if (FAILED(hr)) {
+ return E_NOTIMPL;
+ }
+ IMediaSeeking * pMS;
+ hr = pConnected->QueryInterface(IID_IMediaSeeking, (void **) &pMS);
+ pConnected->Release();
+ if (FAILED(hr)) {
+ return E_NOTIMPL;
+ }
+
+ *ppMS = pMS;
+ return S_OK;
+}
+
+
+// --- IMediaSeeking methods ----------
+
+
+STDMETHODIMP
+CPosPassThru::GetCapabilities(__out DWORD * pCaps)
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMS->GetCapabilities(pCaps);
+ pMS->Release();
+ return hr;
+}
+
+STDMETHODIMP
+CPosPassThru::CheckCapabilities(__inout DWORD * pCaps)
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMS->CheckCapabilities(pCaps);
+ pMS->Release();
+ return hr;
+}
+
+STDMETHODIMP
+CPosPassThru::IsFormatSupported(const GUID * pFormat)
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMS->IsFormatSupported(pFormat);
+ pMS->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::QueryPreferredFormat(__out GUID *pFormat)
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMS->QueryPreferredFormat(pFormat);
+ pMS->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::SetTimeFormat(const GUID * pFormat)
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMS->SetTimeFormat(pFormat);
+ pMS->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::GetTimeFormat(__out GUID *pFormat)
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMS->GetTimeFormat(pFormat);
+ pMS->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::IsUsingTimeFormat(const GUID * pFormat)
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMS->IsUsingTimeFormat(pFormat);
+ pMS->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::ConvertTimeFormat(__out LONGLONG * pTarget,
+ __in_opt const GUID * pTargetFormat,
+ LONGLONG Source,
+ __in_opt const GUID * pSourceFormat )
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMS->ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat );
+ pMS->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::SetPositions( __inout_opt LONGLONG * pCurrent,
+ DWORD CurrentFlags,
+ __inout_opt LONGLONG * pStop,
+ DWORD StopFlags )
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMS->SetPositions(pCurrent, CurrentFlags, pStop, StopFlags );
+ pMS->Release();
+ return hr;
+}
+
+STDMETHODIMP
+CPosPassThru::GetPositions(__out_opt LONGLONG *pCurrent, __out_opt LONGLONG * pStop)
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMS->GetPositions(pCurrent,pStop);
+ pMS->Release();
+ return hr;
+}
+
+HRESULT
+CPosPassThru::GetSeekingLongLong
+( HRESULT (__stdcall IMediaSeeking::*pMethod)( __out LONGLONG * )
+, LONGLONG * pll
+)
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (SUCCEEDED(hr))
+ {
+ hr = (pMS->*pMethod)(pll);
+ pMS->Release();
+ }
+ return hr;
+}
+
+// If we don't have a current position then ask upstream
+
+STDMETHODIMP
+CPosPassThru::GetCurrentPosition(__out LONGLONG *pCurrent)
+{
+ // Can we report the current position
+ HRESULT hr = GetMediaTime(pCurrent,NULL);
+ if (SUCCEEDED(hr)) hr = NOERROR;
+ else hr = GetSeekingLongLong( &IMediaSeeking::GetCurrentPosition, pCurrent );
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::GetStopPosition(__out LONGLONG *pStop)
+{
+ return GetSeekingLongLong( &IMediaSeeking::GetStopPosition, pStop );;
+}
+
+STDMETHODIMP
+CPosPassThru::GetDuration(__out LONGLONG *pDuration)
+{
+ return GetSeekingLongLong( &IMediaSeeking::GetDuration, pDuration );;
+}
+
+
+STDMETHODIMP
+CPosPassThru::GetPreroll(__out LONGLONG *pllPreroll)
+{
+ return GetSeekingLongLong( &IMediaSeeking::GetPreroll, pllPreroll );;
+}
+
+
+STDMETHODIMP
+CPosPassThru::GetAvailable( __out_opt LONGLONG *pEarliest, __out_opt LONGLONG *pLatest )
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMS->GetAvailable( pEarliest, pLatest );
+ pMS->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::GetRate(__out double * pdRate)
+{
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMS->GetRate(pdRate);
+ pMS->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::SetRate(double dRate)
+{
+ if (0.0 == dRate) {
+ return E_INVALIDARG;
+ }
+
+ IMediaSeeking* pMS;
+ HRESULT hr = GetPeerSeeking(&pMS);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMS->SetRate(dRate);
+ pMS->Release();
+ return hr;
+}
+
+
+
+
+// --- IMediaPosition methods ----------
+
+
+STDMETHODIMP
+CPosPassThru::get_Duration(__out REFTIME * plength)
+{
+ IMediaPosition* pMP;
+ HRESULT hr = GetPeer(&pMP);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pMP->get_Duration(plength);
+ pMP->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::get_CurrentPosition(__out REFTIME * pllTime)
+{
+ IMediaPosition* pMP;
+ HRESULT hr = GetPeer(&pMP);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMP->get_CurrentPosition(pllTime);
+ pMP->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::put_CurrentPosition(REFTIME llTime)
+{
+ IMediaPosition* pMP;
+ HRESULT hr = GetPeer(&pMP);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMP->put_CurrentPosition(llTime);
+ pMP->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::get_StopTime(__out REFTIME * pllTime)
+{
+ IMediaPosition* pMP;
+ HRESULT hr = GetPeer(&pMP);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMP->get_StopTime(pllTime);
+ pMP->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::put_StopTime(REFTIME llTime)
+{
+ IMediaPosition* pMP;
+ HRESULT hr = GetPeer(&pMP);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMP->put_StopTime(llTime);
+ pMP->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::get_PrerollTime(__out REFTIME * pllTime)
+{
+ IMediaPosition* pMP;
+ HRESULT hr = GetPeer(&pMP);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMP->get_PrerollTime(pllTime);
+ pMP->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::put_PrerollTime(REFTIME llTime)
+{
+ IMediaPosition* pMP;
+ HRESULT hr = GetPeer(&pMP);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMP->put_PrerollTime(llTime);
+ pMP->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::get_Rate(__out double * pdRate)
+{
+ IMediaPosition* pMP;
+ HRESULT hr = GetPeer(&pMP);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMP->get_Rate(pdRate);
+ pMP->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::put_Rate(double dRate)
+{
+ if (0.0 == dRate) {
+ return E_INVALIDARG;
+ }
+
+ IMediaPosition* pMP;
+ HRESULT hr = GetPeer(&pMP);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMP->put_Rate(dRate);
+ pMP->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::CanSeekForward(__out LONG *pCanSeekForward)
+{
+ IMediaPosition* pMP;
+ HRESULT hr = GetPeer(&pMP);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMP->CanSeekForward(pCanSeekForward);
+ pMP->Release();
+ return hr;
+}
+
+
+STDMETHODIMP
+CPosPassThru::CanSeekBackward(__out LONG *pCanSeekBackward)
+{
+ IMediaPosition* pMP;
+ HRESULT hr = GetPeer(&pMP);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = pMP->CanSeekBackward(pCanSeekBackward);
+ pMP->Release();
+ return hr;
+}
+
+
+// --- Implements the CRendererPosPassThru class ----------
+
+
+// Media times (eg current frame, field, sample etc) are passed through the
+// filtergraph in media samples. When a renderer gets a sample with media
+// times in it, it will call one of the RegisterMediaTime methods we expose
+// (one takes an IMediaSample, the other takes the media times direct). We
+// store the media times internally and return them in GetCurrentPosition.
+
+CRendererPosPassThru::CRendererPosPassThru(__in_opt LPCTSTR pName,
+ __in_opt LPUNKNOWN pUnk,
+ __inout HRESULT *phr,
+ IPin *pPin) :
+ CPosPassThru(pName,pUnk,phr,pPin),
+ m_StartMedia(0),
+ m_EndMedia(0),
+ m_bReset(TRUE)
+{
+}
+
+
+// Sets the media times the object should report
+
+HRESULT
+CRendererPosPassThru::RegisterMediaTime(IMediaSample *pMediaSample)
+{
+ ASSERT(pMediaSample);
+ LONGLONG StartMedia;
+ LONGLONG EndMedia;
+
+ CAutoLock cAutoLock(&m_PositionLock);
+
+ // Get the media times from the sample
+
+ HRESULT hr = pMediaSample->GetTime(&StartMedia,&EndMedia);
+ if (FAILED(hr))
+ {
+ ASSERT(hr == VFW_E_SAMPLE_TIME_NOT_SET);
+ return hr;
+ }
+
+ m_StartMedia = StartMedia;
+ m_EndMedia = EndMedia;
+ m_bReset = FALSE;
+ return NOERROR;
+}
+
+
+// Sets the media times the object should report
+
+HRESULT
+CRendererPosPassThru::RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime)
+{
+ CAutoLock cAutoLock(&m_PositionLock);
+ m_StartMedia = StartTime;
+ m_EndMedia = EndTime;
+ m_bReset = FALSE;
+ return NOERROR;
+}
+
+
+// Return the current media times registered in the object
+
+HRESULT
+CRendererPosPassThru::GetMediaTime(__out LONGLONG *pStartTime, __out_opt LONGLONG *pEndTime)
+{
+ ASSERT(pStartTime);
+
+ CAutoLock cAutoLock(&m_PositionLock);
+ if (m_bReset == TRUE) {
+ return E_FAIL;
+ }
+
+ // We don't have to return the end time
+
+ HRESULT hr = ConvertTimeFormat( pStartTime, 0, m_StartMedia, &TIME_FORMAT_MEDIA_TIME );
+ if (pEndTime && SUCCEEDED(hr)) {
+ hr = ConvertTimeFormat( pEndTime, 0, m_EndMedia, &TIME_FORMAT_MEDIA_TIME );
+ }
+ return hr;
+}
+
+
+// Resets the media times we hold
+
+HRESULT
+CRendererPosPassThru::ResetMediaTime()
+{
+ CAutoLock cAutoLock(&m_PositionLock);
+ m_StartMedia = 0;
+ m_EndMedia = 0;
+ m_bReset = TRUE;
+ return NOERROR;
+}
+
+// Intended to be called by the owing filter during EOS processing so
+// that the media times can be adjusted to the stop time. This ensures
+// that the GetCurrentPosition will actully get to the stop position.
+HRESULT
+CRendererPosPassThru::EOS()
+{
+ HRESULT hr;
+
+ if ( m_bReset == TRUE ) hr = E_FAIL;
+ else
+ {
+ LONGLONG llStop;
+ if SUCCEEDED(hr=GetStopPosition(&llStop))
+ {
+ CAutoLock cAutoLock(&m_PositionLock);
+ m_StartMedia =
+ m_EndMedia = llStop;
+ }
+ }
+ return hr;
+}
+
+// -- CSourceSeeking implementation ------------
+
+CSourceSeeking::CSourceSeeking(
+ __in_opt LPCTSTR pName,
+ __in_opt LPUNKNOWN pUnk,
+ __inout HRESULT* phr,
+ __in CCritSec * pLock) :
+ CUnknown(pName, pUnk),
+ m_pLock(pLock),
+ m_rtStart((long)0)
+{
+ m_rtStop = _I64_MAX / 2;
+ m_rtDuration = m_rtStop;
+ m_dRateSeeking = 1.0;
+
+ m_dwSeekingCaps = AM_SEEKING_CanSeekForwards
+ | AM_SEEKING_CanSeekBackwards
+ | AM_SEEKING_CanSeekAbsolute
+ | AM_SEEKING_CanGetStopPos
+ | AM_SEEKING_CanGetDuration;
+}
+
+HRESULT CSourceSeeking::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ if(riid == IID_IMediaSeeking) {
+ CheckPointer(ppv, E_POINTER);
+ return GetInterface(static_cast(this), ppv);
+ }
+ else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+HRESULT CSourceSeeking::IsFormatSupported(const GUID * pFormat)
+{
+ CheckPointer(pFormat, E_POINTER);
+ // only seeking in time (REFERENCE_TIME units) is supported
+ return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
+}
+
+HRESULT CSourceSeeking::QueryPreferredFormat(__out GUID *pFormat)
+{
+ CheckPointer(pFormat, E_POINTER);
+ *pFormat = TIME_FORMAT_MEDIA_TIME;
+ return S_OK;
+}
+
+HRESULT CSourceSeeking::SetTimeFormat(const GUID * pFormat)
+{
+ CheckPointer(pFormat, E_POINTER);
+
+ // nothing to set; just check that it's TIME_FORMAT_TIME
+ return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : E_INVALIDARG;
+}
+
+HRESULT CSourceSeeking::IsUsingTimeFormat(const GUID * pFormat)
+{
+ CheckPointer(pFormat, E_POINTER);
+ return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
+}
+
+HRESULT CSourceSeeking::GetTimeFormat(__out GUID *pFormat)
+{
+ CheckPointer(pFormat, E_POINTER);
+ *pFormat = TIME_FORMAT_MEDIA_TIME;
+ return S_OK;
+}
+
+HRESULT CSourceSeeking::GetDuration(__out LONGLONG *pDuration)
+{
+ CheckPointer(pDuration, E_POINTER);
+ CAutoLock lock(m_pLock);
+ *pDuration = m_rtDuration;
+ return S_OK;
+}
+
+HRESULT CSourceSeeking::GetStopPosition(__out LONGLONG *pStop)
+{
+ CheckPointer(pStop, E_POINTER);
+ CAutoLock lock(m_pLock);
+ *pStop = m_rtStop;
+ return S_OK;
+}
+
+HRESULT CSourceSeeking::GetCurrentPosition(__out LONGLONG *pCurrent)
+{
+ // GetCurrentPosition is typically supported only in renderers and
+ // not in source filters.
+ return E_NOTIMPL;
+}
+
+HRESULT CSourceSeeking::GetCapabilities( __out DWORD * pCapabilities )
+{
+ CheckPointer(pCapabilities, E_POINTER);
+ *pCapabilities = m_dwSeekingCaps;
+ return S_OK;
+}
+
+HRESULT CSourceSeeking::CheckCapabilities( __inout DWORD * pCapabilities )
+{
+ CheckPointer(pCapabilities, E_POINTER);
+
+ // make sure all requested capabilities are in our mask
+ return (~m_dwSeekingCaps & *pCapabilities) ? S_FALSE : S_OK;
+}
+
+HRESULT CSourceSeeking::ConvertTimeFormat( __out LONGLONG * pTarget,
+ __in_opt const GUID * pTargetFormat,
+ LONGLONG Source,
+ __in_opt const GUID * pSourceFormat )
+{
+ CheckPointer(pTarget, E_POINTER);
+ // format guids can be null to indicate current format
+
+ // since we only support TIME_FORMAT_MEDIA_TIME, we don't really
+ // offer any conversions.
+ if(pTargetFormat == 0 || *pTargetFormat == TIME_FORMAT_MEDIA_TIME)
+ {
+ if(pSourceFormat == 0 || *pSourceFormat == TIME_FORMAT_MEDIA_TIME)
+ {
+ *pTarget = Source;
+ return S_OK;
+ }
+ }
+
+ return E_INVALIDARG;
+}
+
+
+HRESULT CSourceSeeking::SetPositions( __inout_opt LONGLONG * pCurrent,
+ DWORD CurrentFlags,
+ __inout_opt LONGLONG * pStop,
+ DWORD StopFlags )
+{
+ DWORD StopPosBits = StopFlags & AM_SEEKING_PositioningBitsMask;
+ DWORD StartPosBits = CurrentFlags & AM_SEEKING_PositioningBitsMask;
+
+ if(StopFlags) {
+ CheckPointer(pStop, E_POINTER);
+
+ // accept only relative, incremental, or absolute positioning
+ if(StopPosBits != StopFlags) {
+ return E_INVALIDARG;
+ }
+ }
+
+ if(CurrentFlags) {
+ CheckPointer(pCurrent, E_POINTER);
+ if(StartPosBits != AM_SEEKING_AbsolutePositioning &&
+ StartPosBits != AM_SEEKING_RelativePositioning) {
+ return E_INVALIDARG;
+ }
+ }
+
+
+ // scope for autolock
+ {
+ CAutoLock lock(m_pLock);
+
+ // set start position
+ if(StartPosBits == AM_SEEKING_AbsolutePositioning)
+ {
+ m_rtStart = *pCurrent;
+ }
+ else if(StartPosBits == AM_SEEKING_RelativePositioning)
+ {
+ m_rtStart += *pCurrent;
+ }
+
+ // set stop position
+ if(StopPosBits == AM_SEEKING_AbsolutePositioning)
+ {
+ m_rtStop = *pStop;
+ }
+ else if(StopPosBits == AM_SEEKING_IncrementalPositioning)
+ {
+ m_rtStop = m_rtStart + *pStop;
+ }
+ else if(StopPosBits == AM_SEEKING_RelativePositioning)
+ {
+ m_rtStop = m_rtStop + *pStop;
+ }
+ }
+
+
+ HRESULT hr = S_OK;
+ if(SUCCEEDED(hr) && StopPosBits) {
+ hr = ChangeStop();
+ }
+ if(StartPosBits) {
+ hr = ChangeStart();
+ }
+
+ return hr;
+}
+
+
+HRESULT CSourceSeeking::GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop )
+{
+ if(pCurrent) {
+ *pCurrent = m_rtStart;
+ }
+ if(pStop) {
+ *pStop = m_rtStop;
+ }
+
+ return S_OK;;
+}
+
+
+HRESULT CSourceSeeking::GetAvailable( __out_opt LONGLONG * pEarliest, __out_opt LONGLONG * pLatest )
+{
+ if(pEarliest) {
+ *pEarliest = 0;
+ }
+ if(pLatest) {
+ CAutoLock lock(m_pLock);
+ *pLatest = m_rtDuration;
+ }
+ return S_OK;
+}
+
+HRESULT CSourceSeeking::SetRate( double dRate)
+{
+ {
+ CAutoLock lock(m_pLock);
+ m_dRateSeeking = dRate;
+ }
+ return ChangeRate();
+}
+
+HRESULT CSourceSeeking::GetRate( __out double * pdRate)
+{
+ CheckPointer(pdRate, E_POINTER);
+ CAutoLock lock(m_pLock);
+ *pdRate = m_dRateSeeking;
+ return S_OK;
+}
+
+HRESULT CSourceSeeking::GetPreroll(__out LONGLONG *pPreroll)
+{
+ CheckPointer(pPreroll, E_POINTER);
+ *pPreroll = 0;
+ return S_OK;
+}
+
+
+
+
+
+// --- CSourcePosition implementation ----------
+
+
+CSourcePosition::CSourcePosition(__in_opt LPCTSTR pName,
+ __in_opt LPUNKNOWN pUnk,
+ __inout HRESULT* phr,
+ __in CCritSec * pLock) :
+ CMediaPosition(pName, pUnk),
+ m_pLock(pLock),
+ m_Start(CRefTime((LONGLONG)0))
+{
+ m_Stop = _I64_MAX;
+ m_Rate = 1.0;
+}
+
+
+STDMETHODIMP
+CSourcePosition::get_Duration(__out REFTIME * plength)
+{
+ CheckPointer(plength,E_POINTER);
+ ValidateReadWritePtr(plength,sizeof(REFTIME));
+ CAutoLock lock(m_pLock);
+
+ *plength = m_Duration;
+ return S_OK;
+}
+
+
+STDMETHODIMP
+CSourcePosition::put_CurrentPosition(REFTIME llTime)
+{
+ m_pLock->Lock();
+ m_Start = llTime;
+ m_pLock->Unlock();
+
+ return ChangeStart();
+}
+
+
+STDMETHODIMP
+CSourcePosition::get_StopTime(__out REFTIME * pllTime)
+{
+ CheckPointer(pllTime,E_POINTER);
+ ValidateReadWritePtr(pllTime,sizeof(REFTIME));
+ CAutoLock lock(m_pLock);
+
+ *pllTime = m_Stop;
+ return S_OK;
+}
+
+
+STDMETHODIMP
+CSourcePosition::put_StopTime(REFTIME llTime)
+{
+ m_pLock->Lock();
+ m_Stop = llTime;
+ m_pLock->Unlock();
+
+ return ChangeStop();
+}
+
+
+STDMETHODIMP
+CSourcePosition::get_PrerollTime(__out REFTIME * pllTime)
+{
+ CheckPointer(pllTime,E_POINTER);
+ ValidateReadWritePtr(pllTime,sizeof(REFTIME));
+ return E_NOTIMPL;
+}
+
+
+STDMETHODIMP
+CSourcePosition::put_PrerollTime(REFTIME llTime)
+{
+ return E_NOTIMPL;
+}
+
+
+STDMETHODIMP
+CSourcePosition::get_Rate(__out double * pdRate)
+{
+ CheckPointer(pdRate,E_POINTER);
+ ValidateReadWritePtr(pdRate,sizeof(double));
+ CAutoLock lock(m_pLock);
+
+ *pdRate = m_Rate;
+ return S_OK;
+}
+
+
+STDMETHODIMP
+CSourcePosition::put_Rate(double dRate)
+{
+ m_pLock->Lock();
+ m_Rate = dRate;
+ m_pLock->Unlock();
+
+ return ChangeRate();
+}
+
+
+// By default we can seek forwards
+
+STDMETHODIMP
+CSourcePosition::CanSeekForward(__out LONG *pCanSeekForward)
+{
+ CheckPointer(pCanSeekForward,E_POINTER);
+ *pCanSeekForward = OATRUE;
+ return S_OK;
+}
+
+
+// By default we can seek backwards
+
+STDMETHODIMP
+CSourcePosition::CanSeekBackward(__out LONG *pCanSeekBackward)
+{
+ CheckPointer(pCanSeekBackward,E_POINTER);
+ *pCanSeekBackward = OATRUE;
+ return S_OK;
+}
+
+
+// --- Implementation of CBasicAudio class ----------
+
+
+CBasicAudio::CBasicAudio(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :
+ CUnknown(pName, punk)
+{
+}
+
+// overriden to publicise our interfaces
+
+STDMETHODIMP
+CBasicAudio::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ ValidateReadWritePtr(ppv,sizeof(PVOID));
+ if (riid == IID_IBasicAudio) {
+ return GetInterface( (IBasicAudio *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+STDMETHODIMP
+CBasicAudio::GetTypeInfoCount(__out UINT * pctinfo)
+{
+ return m_basedisp.GetTypeInfoCount(pctinfo);
+}
+
+
+STDMETHODIMP
+CBasicAudio::GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo)
+{
+ return m_basedisp.GetTypeInfo(
+ IID_IBasicAudio,
+ itinfo,
+ lcid,
+ pptinfo);
+}
+
+
+STDMETHODIMP
+CBasicAudio::GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid)
+{
+ return m_basedisp.GetIDsOfNames(
+ IID_IBasicAudio,
+ rgszNames,
+ cNames,
+ lcid,
+ rgdispid);
+}
+
+
+STDMETHODIMP
+CBasicAudio::Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr)
+{
+ // this parameter is a dead leftover from an earlier interface
+ if (IID_NULL != riid) {
+ return DISP_E_UNKNOWNINTERFACE;
+ }
+
+ ITypeInfo * pti;
+ HRESULT hr = GetTypeInfo(0, lcid, &pti);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pti->Invoke(
+ (IBasicAudio *)this,
+ dispidMember,
+ wFlags,
+ pdispparams,
+ pvarResult,
+ pexcepinfo,
+ puArgErr);
+
+ pti->Release();
+ return hr;
+}
+
+
+// --- IVideoWindow implementation ----------
+
+CBaseVideoWindow::CBaseVideoWindow(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :
+ CUnknown(pName, punk)
+{
+}
+
+
+// overriden to publicise our interfaces
+
+STDMETHODIMP
+CBaseVideoWindow::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ ValidateReadWritePtr(ppv,sizeof(PVOID));
+ if (riid == IID_IVideoWindow) {
+ return GetInterface( (IVideoWindow *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+STDMETHODIMP
+CBaseVideoWindow::GetTypeInfoCount(__out UINT * pctinfo)
+{
+ return m_basedisp.GetTypeInfoCount(pctinfo);
+}
+
+
+STDMETHODIMP
+CBaseVideoWindow::GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo)
+{
+ return m_basedisp.GetTypeInfo(
+ IID_IVideoWindow,
+ itinfo,
+ lcid,
+ pptinfo);
+}
+
+
+STDMETHODIMP
+CBaseVideoWindow::GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid)
+{
+ return m_basedisp.GetIDsOfNames(
+ IID_IVideoWindow,
+ rgszNames,
+ cNames,
+ lcid,
+ rgdispid);
+}
+
+
+STDMETHODIMP
+CBaseVideoWindow::Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr)
+{
+ // this parameter is a dead leftover from an earlier interface
+ if (IID_NULL != riid) {
+ return DISP_E_UNKNOWNINTERFACE;
+ }
+
+ ITypeInfo * pti;
+ HRESULT hr = GetTypeInfo(0, lcid, &pti);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pti->Invoke(
+ (IVideoWindow *)this,
+ dispidMember,
+ wFlags,
+ pdispparams,
+ pvarResult,
+ pexcepinfo,
+ puArgErr);
+
+ pti->Release();
+ return hr;
+}
+
+
+// --- IBasicVideo implementation ----------
+
+
+CBaseBasicVideo::CBaseBasicVideo(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :
+ CUnknown(pName, punk)
+{
+}
+
+
+// overriden to publicise our interfaces
+
+STDMETHODIMP
+CBaseBasicVideo::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ ValidateReadWritePtr(ppv,sizeof(PVOID));
+ if (riid == IID_IBasicVideo || riid == IID_IBasicVideo2) {
+ return GetInterface( static_cast(this), ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+STDMETHODIMP
+CBaseBasicVideo::GetTypeInfoCount(__out UINT * pctinfo)
+{
+ return m_basedisp.GetTypeInfoCount(pctinfo);
+}
+
+
+STDMETHODIMP
+CBaseBasicVideo::GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo)
+{
+ return m_basedisp.GetTypeInfo(
+ IID_IBasicVideo,
+ itinfo,
+ lcid,
+ pptinfo);
+}
+
+
+STDMETHODIMP
+CBaseBasicVideo::GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid)
+{
+ return m_basedisp.GetIDsOfNames(
+ IID_IBasicVideo,
+ rgszNames,
+ cNames,
+ lcid,
+ rgdispid);
+}
+
+
+STDMETHODIMP
+CBaseBasicVideo::Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr)
+{
+ // this parameter is a dead leftover from an earlier interface
+ if (IID_NULL != riid) {
+ return DISP_E_UNKNOWNINTERFACE;
+ }
+
+ ITypeInfo * pti;
+ HRESULT hr = GetTypeInfo(0, lcid, &pti);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = pti->Invoke(
+ (IBasicVideo *)this,
+ dispidMember,
+ wFlags,
+ pdispparams,
+ pvarResult,
+ pexcepinfo,
+ puArgErr);
+
+ pti->Release();
+ return hr;
+}
+
+
+// --- Implementation of Deferred Commands ----------
+
+
+CDispParams::CDispParams(UINT nArgs, __in_ecount(nArgs) VARIANT* pArgs, __inout_opt HRESULT *phr)
+{
+ cNamedArgs = 0;
+ rgdispidNamedArgs = NULL;
+ cArgs = nArgs;
+
+ if (cArgs) {
+ rgvarg = new VARIANT[cArgs];
+ if (NULL == rgvarg) {
+ cArgs = 0;
+ if (phr) {
+ *phr = E_OUTOFMEMORY;
+ }
+ return;
+ }
+
+ for (UINT i = 0; i < cArgs; i++) {
+
+ // Why aren't we using VariantCopy?
+
+ VARIANT * pDest = &rgvarg[i];
+ VARIANT * pSrc = &pArgs[i];
+
+ pDest->vt = pSrc->vt;
+ switch(pDest->vt) {
+
+ case VT_I4:
+ pDest->lVal = pSrc->lVal;
+ break;
+
+ case VT_UI1:
+ pDest->bVal = pSrc->bVal;
+ break;
+
+ case VT_I2:
+ pDest->iVal = pSrc->iVal;
+ break;
+
+ case VT_R4:
+ pDest->fltVal = pSrc->fltVal;
+ break;
+
+ case VT_R8:
+ pDest->dblVal = pSrc->dblVal;
+ break;
+
+ case VT_BOOL:
+ pDest->boolVal = pSrc->boolVal;
+ break;
+
+ case VT_ERROR:
+ pDest->scode = pSrc->scode;
+ break;
+
+ case VT_CY:
+ pDest->cyVal = pSrc->cyVal;
+ break;
+
+ case VT_DATE:
+ pDest->date = pSrc->date;
+ break;
+
+ case VT_BSTR:
+ if ((PVOID)pSrc->bstrVal == NULL) {
+ pDest->bstrVal = NULL;
+ } else {
+
+ // a BSTR is a WORD followed by a UNICODE string.
+ // the pointer points just after the WORD
+
+ WORD len = * (WORD*) (pSrc->bstrVal - (sizeof(WORD) / sizeof(OLECHAR)));
+ OLECHAR* pch = new OLECHAR[len + (sizeof(WORD)/sizeof(OLECHAR))];
+ if (pch) {
+ WORD *pui = (WORD*)pch;
+ *pui = len;
+ pDest->bstrVal = pch + (sizeof(WORD)/sizeof(OLECHAR));
+ CopyMemory(pDest->bstrVal, pSrc->bstrVal, len*sizeof(OLECHAR));
+ } else {
+ cArgs = i;
+ if (phr) {
+ *phr = E_OUTOFMEMORY;
+ }
+ }
+ }
+ break;
+
+ case VT_UNKNOWN:
+ pDest->punkVal = pSrc->punkVal;
+ pDest->punkVal->AddRef();
+ break;
+
+ case VT_DISPATCH:
+ pDest->pdispVal = pSrc->pdispVal;
+ pDest->pdispVal->AddRef();
+ break;
+
+ default:
+ // a type we haven't got round to adding yet!
+ ASSERT(0);
+ break;
+ }
+ }
+
+ } else {
+ rgvarg = NULL;
+ }
+
+}
+
+
+CDispParams::~CDispParams()
+{
+ for (UINT i = 0; i < cArgs; i++) {
+ switch(rgvarg[i].vt) {
+ case VT_BSTR:
+ // Explicitly cast BSTR to PVOID to tell code scanning tools we really mean to test the pointer
+ if ((PVOID)rgvarg[i].bstrVal != NULL) {
+ OLECHAR * pch = rgvarg[i].bstrVal - (sizeof(WORD)/sizeof(OLECHAR));
+ delete pch;
+ }
+ break;
+
+ case VT_UNKNOWN:
+ rgvarg[i].punkVal->Release();
+ break;
+
+ case VT_DISPATCH:
+ rgvarg[i].pdispVal->Release();
+ break;
+ }
+ }
+ delete[] rgvarg;
+}
+
+
+// lifetime is controlled by refcounts (see defer.h)
+
+CDeferredCommand::CDeferredCommand(
+ __inout CCmdQueue * pQ,
+ __in_opt LPUNKNOWN pUnk,
+ __inout HRESULT * phr,
+ __in LPUNKNOWN pUnkExecutor,
+ REFTIME time,
+ __in GUID* iid,
+ long dispidMethod,
+ short wFlags,
+ long nArgs,
+ __in_ecount(nArgs) VARIANT* pDispParams,
+ __out VARIANT* pvarResult,
+ __out short* puArgErr,
+ BOOL bStream
+ ) :
+ CUnknown(NAME("DeferredCommand"), pUnk),
+ m_pQueue(pQ),
+ m_pUnk(pUnkExecutor),
+ m_iid(iid),
+ m_dispidMethod(dispidMethod),
+ m_wFlags(wFlags),
+ m_DispParams(nArgs, pDispParams, phr),
+ m_pvarResult(pvarResult),
+ m_bStream(bStream),
+ m_hrResult(E_ABORT)
+
+{
+ // convert REFTIME to REFERENCE_TIME
+ COARefTime convertor(time);
+ m_time = convertor;
+
+ // no check of time validity - it's ok to queue a command that's
+ // already late
+
+ // check iid is supportable on pUnk by QueryInterface for it
+ IUnknown * pInterface;
+ HRESULT hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);
+ if (FAILED(hr)) {
+ *phr = hr;
+ return;
+ }
+ pInterface->Release();
+
+
+ // !!! check dispidMethod and param/return types using typelib
+ ITypeInfo *pti;
+ hr = m_Dispatch.GetTypeInfo(*iid, 0, 0, &pti);
+ if (FAILED(hr)) {
+ *phr = hr;
+ return;
+ }
+ // !!! some sort of ITypeInfo validity check here
+ pti->Release();
+
+
+ // Fix up the dispid for put and get
+ if (wFlags == DISPATCH_PROPERTYPUT) {
+ m_DispParams.cNamedArgs = 1;
+ m_DispId = DISPID_PROPERTYPUT;
+ m_DispParams.rgdispidNamedArgs = &m_DispId;
+ }
+
+ // all checks ok - add to queue
+ hr = pQ->Insert(this);
+ if (FAILED(hr)) {
+ *phr = hr;
+ }
+}
+
+
+// refcounts are held by caller of InvokeAt... and by list. So if
+// we get here, we can't be on the list
+
+#if 0
+CDeferredCommand::~CDeferredCommand()
+{
+ // this assert is invalid since if the queue is deleted while we are
+ // still on the queue, we will have been removed by the queue and this
+ // m_pQueue will not have been modified.
+ // ASSERT(m_pQueue == NULL);
+
+ // we don't hold a ref count on pUnk, which is the object that should
+ // execute the command.
+ // This is because there would otherwise be a circular refcount problem
+ // since pUnk probably owns the CmdQueue object that has a refcount
+ // on us.
+ // The lifetime of pUnk is guaranteed by it being part of, or lifetime
+ // controlled by, our parent object. As long as we are on the list, pUnk
+ // must be valid. Once we are off the list, we do not use pUnk.
+
+}
+#endif
+
+
+// overriden to publicise our interfaces
+
+STDMETHODIMP
+CDeferredCommand::NonDelegatingQueryInterface(REFIID riid, __out void **ppv)
+{
+ ValidateReadWritePtr(ppv,sizeof(PVOID));
+ if (riid == IID_IDeferredCommand) {
+ return GetInterface( (IDeferredCommand *) this, ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+// remove from q. this will reduce the refcount by one (since the q
+// holds a count) but can't make us go away since he must have a
+// refcount in order to call this method.
+
+STDMETHODIMP
+CDeferredCommand::Cancel()
+{
+ if (m_pQueue == NULL) {
+ return VFW_E_ALREADY_CANCELLED;
+ }
+
+ HRESULT hr = m_pQueue->Remove(this);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ m_pQueue = NULL;
+ return S_OK;
+}
+
+
+STDMETHODIMP
+CDeferredCommand::Confidence(__out LONG* pConfidence)
+{
+ return E_NOTIMPL;
+}
+
+
+STDMETHODIMP
+CDeferredCommand::GetHResult(__out HRESULT * phrResult)
+{
+ CheckPointer(phrResult,E_POINTER);
+ ValidateReadWritePtr(phrResult,sizeof(HRESULT));
+
+ if (m_pQueue != NULL) {
+ return E_ABORT;
+ }
+ *phrResult = m_hrResult;
+ return S_OK;
+}
+
+
+// set the time to be a new time (checking that it is valid) and
+// then requeue
+
+STDMETHODIMP
+CDeferredCommand::Postpone(REFTIME newtime)
+{
+
+ // check that this time is not past
+ // convert REFTIME to REFERENCE_TIME
+ COARefTime convertor(newtime);
+
+ // check that the time has not passed
+ if (m_pQueue->CheckTime(convertor, IsStreamTime())) {
+ return VFW_E_TIME_ALREADY_PASSED;
+ }
+
+ // extract from list
+ HRESULT hr = m_pQueue->Remove(this);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // change time
+ m_time = convertor;
+
+ // requeue
+ hr = m_pQueue->Insert(this);
+
+ return hr;
+}
+
+
+HRESULT
+CDeferredCommand::Invoke()
+{
+ // check that we are still outstanding
+ if (m_pQueue == NULL) {
+ return VFW_E_ALREADY_CANCELLED;
+ }
+
+ // get the type info
+ ITypeInfo* pti;
+ HRESULT hr = m_Dispatch.GetTypeInfo(GetIID(), 0, 0, &pti);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // qi for the expected interface and then invoke it. Note that we have to
+ // treat the returned interface as IUnknown since we don't know its type.
+ IUnknown* pInterface;
+
+ hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);
+ if (FAILED(hr)) {
+ pti->Release();
+ return hr;
+ }
+
+ EXCEPINFO expinfo;
+ UINT uArgErr;
+ m_hrResult = pti->Invoke(
+ pInterface,
+ GetMethod(),
+ GetFlags(),
+ GetParams(),
+ GetResult(),
+ &expinfo,
+ &uArgErr);
+
+ // release the interface we QI'd for
+ pInterface->Release();
+ pti->Release();
+
+
+ // remove from list whether or not successful
+ // or we loop indefinitely
+ hr = m_pQueue->Remove(this);
+ m_pQueue = NULL;
+ return hr;
+}
+
+
+
+// --- CCmdQueue methods ----------
+
+
+CCmdQueue::CCmdQueue(__inout_opt HRESULT *phr) :
+ m_listPresentation(NAME("Presentation time command list")),
+ m_listStream(NAME("Stream time command list")),
+ m_evDue(TRUE, phr), // manual reset
+ m_dwAdvise(0),
+ m_pClock(NULL),
+ m_bRunning(FALSE)
+{
+}
+
+
+CCmdQueue::~CCmdQueue()
+{
+ // empty all our lists
+
+ // we hold a refcount on each, so traverse and Release each
+ // entry then RemoveAll to empty the list
+ POSITION pos = m_listPresentation.GetHeadPosition();
+
+ while(pos) {
+ CDeferredCommand* pCmd = m_listPresentation.GetNext(pos);
+ pCmd->Release();
+ }
+ m_listPresentation.RemoveAll();
+
+ pos = m_listStream.GetHeadPosition();
+
+ while(pos) {
+ CDeferredCommand* pCmd = m_listStream.GetNext(pos);
+ pCmd->Release();
+ }
+ m_listStream.RemoveAll();
+
+ if (m_pClock) {
+ if (m_dwAdvise) {
+ m_pClock->Unadvise(m_dwAdvise);
+ m_dwAdvise = 0;
+ }
+ m_pClock->Release();
+ }
+}
+
+
+// returns a new CDeferredCommand object that will be initialised with
+// the parameters and will be added to the queue during construction.
+// returns S_OK if successfully created otherwise an error and
+// no object has been queued.
+
+HRESULT
+CCmdQueue::New(
+ __out CDeferredCommand **ppCmd,
+ __in LPUNKNOWN pUnk, // this object will execute command
+ REFTIME time,
+ __in GUID* iid,
+ long dispidMethod,
+ short wFlags,
+ long cArgs,
+ __in_ecount(cArgs) VARIANT* pDispParams,
+ __out VARIANT* pvarResult,
+ __out short* puArgErr,
+ BOOL bStream
+)
+{
+ CAutoLock lock(&m_Lock);
+
+ HRESULT hr = S_OK;
+ *ppCmd = NULL;
+
+ CDeferredCommand* pCmd;
+ pCmd = new CDeferredCommand(
+ this,
+ NULL, // not aggregated
+ &hr,
+ pUnk, // this guy will execute
+ time,
+ iid,
+ dispidMethod,
+ wFlags,
+ cArgs,
+ pDispParams,
+ pvarResult,
+ puArgErr,
+ bStream);
+
+ if (pCmd == NULL) {
+ hr = E_OUTOFMEMORY;
+ } else {
+ *ppCmd = pCmd;
+ }
+ return hr;
+}
+
+
+HRESULT
+CCmdQueue::Insert(__in CDeferredCommand* pCmd)
+{
+ CAutoLock lock(&m_Lock);
+
+ // addref the item
+ pCmd->AddRef();
+
+ CGenericList * pList;
+ if (pCmd->IsStreamTime()) {
+ pList = &m_listStream;
+ } else {
+ pList = &m_listPresentation;
+ }
+ POSITION pos = pList->GetHeadPosition();
+
+ // seek past all items that are before us
+ while (pos &&
+ (pList->GetValid(pos)->GetTime() <= pCmd->GetTime())) {
+
+ pList->GetNext(pos);
+ }
+
+ // now at end of list or in front of items that come later
+ if (!pos) {
+ pList->AddTail(pCmd);
+ } else {
+ pList->AddBefore(pos, pCmd);
+ }
+
+ SetTimeAdvise();
+ return S_OK;
+}
+
+
+HRESULT
+CCmdQueue::Remove(__in CDeferredCommand* pCmd)
+{
+ CAutoLock lock(&m_Lock);
+ HRESULT hr = S_OK;
+
+ CGenericList * pList;
+ if (pCmd->IsStreamTime()) {
+ pList = &m_listStream;
+ } else {
+ pList = &m_listPresentation;
+ }
+ POSITION pos = pList->GetHeadPosition();
+
+ // traverse the list
+ while (pos && (pList->GetValid(pos) != pCmd)) {
+ pList->GetNext(pos);
+ }
+
+ // did we drop off the end?
+ if (!pos) {
+ hr = VFW_E_NOT_FOUND;
+ } else {
+
+ // found it - now take off list
+ pList->Remove(pos);
+
+ // Insert did an AddRef, so release it
+ pCmd->Release();
+
+ // check that timer request is still for earliest time
+ SetTimeAdvise();
+ }
+ return hr;
+}
+
+
+// set the clock used for timing
+
+HRESULT
+CCmdQueue::SetSyncSource(__in_opt IReferenceClock* pClock)
+{
+ CAutoLock lock(&m_Lock);
+
+ // addref the new clock first in case they are the same
+ if (pClock) {
+ pClock->AddRef();
+ }
+
+ // kill any advise on the old clock
+ if (m_pClock) {
+ if (m_dwAdvise) {
+ m_pClock->Unadvise(m_dwAdvise);
+ m_dwAdvise = 0;
+ }
+ m_pClock->Release();
+ }
+ m_pClock = pClock;
+
+ // set up a new advise
+ SetTimeAdvise();
+ return S_OK;
+}
+
+
+// set up a timer event with the reference clock
+
+void
+CCmdQueue::SetTimeAdvise(void)
+{
+ // make sure we have a clock to use
+ if (!m_pClock) {
+ return;
+ }
+
+ // reset the event whenever we are requesting a new signal
+ m_evDue.Reset();
+
+ // time 0 is earliest
+ CRefTime current;
+
+ // find the earliest presentation time
+ POSITION pos = m_listPresentation.GetHeadPosition();
+ if (pos != NULL) {
+ current = m_listPresentation.GetValid(pos)->GetTime();
+ }
+
+ // if we're running, check the stream times too
+ if (m_bRunning) {
+
+ CRefTime t;
+ pos = m_listStream.GetHeadPosition();
+ if (NULL != pos) {
+ t = m_listStream.GetValid(pos)->GetTime();
+
+ // add on stream time offset to get presentation time
+ t += m_StreamTimeOffset;
+
+ // is this earlier?
+ if ((current == TimeZero) || (t < current)) {
+ current = t;
+ }
+ }
+ }
+
+ // need to change?
+ if ((current > TimeZero) && (current != m_tCurrentAdvise)) {
+ if (m_dwAdvise) {
+ m_pClock->Unadvise(m_dwAdvise);
+ // reset the event whenever we are requesting a new signal
+ m_evDue.Reset();
+ }
+
+ // ask for time advice - the first two params are either
+ // stream time offset and stream time or
+ // presentation time and 0. we always use the latter
+ HRESULT hr = m_pClock->AdviseTime(
+ (REFERENCE_TIME)current,
+ TimeZero,
+ (HEVENT) HANDLE(m_evDue),
+ &m_dwAdvise);
+
+ ASSERT(SUCCEEDED(hr));
+ m_tCurrentAdvise = current;
+ }
+}
+
+
+// switch to run mode. Streamtime to Presentation time mapping known.
+
+HRESULT
+CCmdQueue::Run(REFERENCE_TIME tStreamTimeOffset)
+{
+ CAutoLock lock(&m_Lock);
+
+ m_StreamTimeOffset = tStreamTimeOffset;
+ m_bRunning = TRUE;
+
+ // ensure advise is accurate
+ SetTimeAdvise();
+ return S_OK;
+}
+
+
+// switch to Stopped or Paused mode. Time mapping not known.
+
+HRESULT
+CCmdQueue::EndRun()
+{
+ CAutoLock lock(&m_Lock);
+
+ m_bRunning = FALSE;
+
+ // check timer setting - stream times
+ SetTimeAdvise();
+ return S_OK;
+}
+
+
+// return a pointer to the next due command. Blocks for msTimeout
+// milliseconds until there is a due command.
+// Stream-time commands will only become due between Run and Endrun calls.
+// The command remains queued until invoked or cancelled.
+// Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).
+//
+// returns an AddRef'd object
+
+HRESULT
+CCmdQueue::GetDueCommand(__out CDeferredCommand ** ppCmd, long msTimeout)
+{
+ // loop until we timeout or find a due command
+ for (;;) {
+
+ {
+ CAutoLock lock(&m_Lock);
+
+
+ // find the earliest command
+ CDeferredCommand * pCmd = NULL;
+
+ // check the presentation time and the
+ // stream time list to find the earliest
+
+ POSITION pos = m_listPresentation.GetHeadPosition();
+
+ if (NULL != pos) {
+ pCmd = m_listPresentation.GetValid(pos);
+ }
+
+ if (m_bRunning) {
+ pos = m_listStream.GetHeadPosition();
+ if (NULL != pos) {
+ CDeferredCommand* pStrm = m_listStream.GetValid(pos);
+
+ CRefTime t = pStrm->GetTime() + m_StreamTimeOffset;
+ if (!pCmd || (t < pCmd->GetTime())) {
+ pCmd = pStrm;
+ }
+ }
+ }
+
+ // if we have found one, is it due?
+ if (pCmd) {
+ if (CheckTime(pCmd->GetTime(), pCmd->IsStreamTime())) {
+
+ // yes it's due - addref it
+ pCmd->AddRef();
+ *ppCmd = pCmd;
+ return S_OK;
+ }
+ }
+ }
+
+ // block until the advise is signalled
+ if (WaitForSingleObject(m_evDue, msTimeout) != WAIT_OBJECT_0) {
+ return E_ABORT;
+ }
+ }
+}
+
+
+// return a pointer to a command that will be due for a given time.
+// Pass in a stream time here. The stream time offset will be passed
+// in via the Run method.
+// Commands remain queued until invoked or cancelled.
+// This method will not block. It will report E_ABORT if there are no
+// commands due yet.
+//
+// returns an AddRef'd object
+
+HRESULT
+CCmdQueue::GetCommandDueFor(REFERENCE_TIME rtStream, __out CDeferredCommand**ppCmd)
+{
+ CAutoLock lock(&m_Lock);
+
+ CRefTime tStream(rtStream);
+
+ // find the earliest stream and presentation time commands
+ CDeferredCommand* pStream = NULL;
+ POSITION pos = m_listStream.GetHeadPosition();
+ if (NULL != pos) {
+ pStream = m_listStream.GetValid(pos);
+ }
+ CDeferredCommand* pPresent = NULL;
+ pos = m_listPresentation.GetHeadPosition();
+ if (NULL != pos) {
+ pPresent = m_listPresentation.GetValid(pos);
+ }
+
+ // is there a presentation time that has passed already
+ if (pPresent && CheckTime(pPresent->GetTime(), FALSE)) {
+ pPresent->AddRef();
+ *ppCmd = pPresent;
+ return S_OK;
+ }
+
+ // is there a stream time command due before this stream time
+ if (pStream && (pStream->GetTime() <= tStream)) {
+ pStream->AddRef();
+ *ppCmd = pStream;
+ return S_OK;
+ }
+
+ // if we are running, we can map presentation times to
+ // stream time. In this case, is there a presentation time command
+ // that will be due before this stream time is presented?
+ if (m_bRunning && pPresent) {
+
+ // this stream time will appear at...
+ tStream += m_StreamTimeOffset;
+
+ // due before that?
+ if (pPresent->GetTime() <= tStream) {
+ *ppCmd = pPresent;
+ return S_OK;
+ }
+ }
+
+ // no commands due yet
+ return VFW_E_NOT_FOUND;
+}
+
diff --git a/dshow_base/ctlutil.h b/dshow_base/ctlutil.h
index 3d45e8c..7e4719c 100644
--- a/dshow_base/ctlutil.h
+++ b/dshow_base/ctlutil.h
@@ -1,919 +1,923 @@
-//------------------------------------------------------------------------------
-// File: CtlUtil.h
-//
-// Desc: DirectShow base classes.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-// Base classes implementing IDispatch parsing for the basic control dual
-// interfaces. Derive from these and implement just the custom method and
-// property methods. We also implement CPosPassThru that can be used by
-// renderers and transforms to pass by IMediaPosition and IMediaSeeking
-
-#ifndef __CTLUTIL__
-#define __CTLUTIL__
-
-// OLE Automation has different ideas of TRUE and FALSE
-
-#define OATRUE (-1)
-#define OAFALSE (0)
-
-
-// It's possible that we could replace this class with CreateStdDispatch
-
-class CBaseDispatch
-{
- ITypeInfo * m_pti;
-
-public:
-
- CBaseDispatch() : m_pti(NULL) {}
- ~CBaseDispatch();
-
- /* IDispatch methods */
- STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
-
- STDMETHODIMP GetTypeInfo(
- REFIID riid,
- UINT itinfo,
- LCID lcid,
- ITypeInfo ** pptinfo);
-
- STDMETHODIMP GetIDsOfNames(
- REFIID riid,
- OLECHAR ** rgszNames,
- UINT cNames,
- LCID lcid,
- DISPID * rgdispid);
-};
-
-
-class AM_NOVTABLE CMediaControl :
- public IMediaControl,
- public CUnknown
-{
- CBaseDispatch m_basedisp;
-
-public:
-
- CMediaControl(const TCHAR *, LPUNKNOWN);
-
- DECLARE_IUNKNOWN
-
- // override this to publicise our interfaces
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- /* IDispatch methods */
- STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
-
- STDMETHODIMP GetTypeInfo(
- UINT itinfo,
- LCID lcid,
- ITypeInfo ** pptinfo);
-
- STDMETHODIMP GetIDsOfNames(
- REFIID riid,
- OLECHAR ** rgszNames,
- UINT cNames,
- LCID lcid,
- DISPID * rgdispid);
-
- STDMETHODIMP Invoke(
- DISPID dispidMember,
- REFIID riid,
- LCID lcid,
- WORD wFlags,
- DISPPARAMS * pdispparams,
- VARIANT * pvarResult,
- EXCEPINFO * pexcepinfo,
- UINT * puArgErr);
-};
-
-
-class AM_NOVTABLE CMediaEvent :
- public IMediaEventEx,
- public CUnknown
-{
- CBaseDispatch m_basedisp;
-
-public:
-
- CMediaEvent(const TCHAR *, LPUNKNOWN);
-
- DECLARE_IUNKNOWN
-
- // override this to publicise our interfaces
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- /* IDispatch methods */
- STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
-
- STDMETHODIMP GetTypeInfo(
- UINT itinfo,
- LCID lcid,
- ITypeInfo ** pptinfo);
-
- STDMETHODIMP GetIDsOfNames(
- REFIID riid,
- OLECHAR ** rgszNames,
- UINT cNames,
- LCID lcid,
- DISPID * rgdispid);
-
- STDMETHODIMP Invoke(
- DISPID dispidMember,
- REFIID riid,
- LCID lcid,
- WORD wFlags,
- DISPPARAMS * pdispparams,
- VARIANT * pvarResult,
- EXCEPINFO * pexcepinfo,
- UINT * puArgErr);
-};
-
-
-class AM_NOVTABLE CMediaPosition :
- public IMediaPosition,
- public CUnknown
-{
- CBaseDispatch m_basedisp;
-
-
-public:
-
- CMediaPosition(const TCHAR *, LPUNKNOWN);
- CMediaPosition(const TCHAR *, LPUNKNOWN, HRESULT *phr);
-
- DECLARE_IUNKNOWN
-
- // override this to publicise our interfaces
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- /* IDispatch methods */
- STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
-
- STDMETHODIMP GetTypeInfo(
- UINT itinfo,
- LCID lcid,
- ITypeInfo ** pptinfo);
-
- STDMETHODIMP GetIDsOfNames(
- REFIID riid,
- OLECHAR ** rgszNames,
- UINT cNames,
- LCID lcid,
- DISPID * rgdispid);
-
- STDMETHODIMP Invoke(
- DISPID dispidMember,
- REFIID riid,
- LCID lcid,
- WORD wFlags,
- DISPPARAMS * pdispparams,
- VARIANT * pvarResult,
- EXCEPINFO * pexcepinfo,
- UINT * puArgErr);
-
-};
-
-
-// OA-compatibility means that we must use double as the RefTime value,
-// and REFERENCE_TIME (essentially a LONGLONG) within filters.
-// this class converts between the two
-
-class COARefTime : public CRefTime {
-public:
-
- COARefTime() {
- };
-
- COARefTime(CRefTime t)
- : CRefTime(t)
- {
- };
-
- COARefTime(REFERENCE_TIME t)
- : CRefTime(t)
- {
- };
-
- COARefTime(double d) {
- m_time = (LONGLONG) (d * 10000000);
- };
-
- operator double() {
- return double(m_time) / 10000000;
- };
-
- operator REFERENCE_TIME() {
- return m_time;
- };
-
- COARefTime& operator=(const double& rd) {
- m_time = (LONGLONG) (rd * 10000000);
- return *this;
- }
-
- COARefTime& operator=(const REFERENCE_TIME& rt) {
- m_time = rt;
- return *this;
- }
-
- inline BOOL operator==(const COARefTime& rt)
- {
- return m_time == rt.m_time;
- };
-
- inline BOOL operator!=(const COARefTime& rt)
- {
- return m_time != rt.m_time;
- };
-
- inline BOOL operator < (const COARefTime& rt)
- {
- return m_time < rt.m_time;
- };
-
- inline BOOL operator > (const COARefTime& rt)
- {
- return m_time > rt.m_time;
- };
-
- inline BOOL operator >= (const COARefTime& rt)
- {
- return m_time >= rt.m_time;
- };
-
- inline BOOL operator <= (const COARefTime& rt)
- {
- return m_time <= rt.m_time;
- };
-
- inline COARefTime operator+(const COARefTime& rt)
- {
- return COARefTime(m_time + rt.m_time);
- };
-
- inline COARefTime operator-(const COARefTime& rt)
- {
- return COARefTime(m_time - rt.m_time);
- };
-
- inline COARefTime operator*(LONG l)
- {
- return COARefTime(m_time * l);
- };
-
- inline COARefTime operator/(LONG l)
- {
- return COARefTime(m_time / l);
- };
-
-private:
- // Prevent bugs from constructing from LONG (which gets
- // converted to double and then multiplied by 10000000
- COARefTime(LONG);
- COARefTime &operator=(LONG);
-};
-
-
-// A utility class that handles IMediaPosition and IMediaSeeking on behalf
-// of single-input pin renderers, or transform filters.
-//
-// Renderers will expose this from the filter; transform filters will
-// expose it from the output pin and not the renderer.
-//
-// Create one of these, giving it your IPin* for your input pin, and delegate
-// all IMediaPosition methods to it. It will query the input pin for
-// IMediaPosition and respond appropriately.
-//
-// Call ForceRefresh if the pin connection changes.
-//
-// This class no longer caches the upstream IMediaPosition or IMediaSeeking
-// it acquires it on each method call. This means ForceRefresh is not needed.
-// The method is kept for source compatibility and to minimise the changes
-// if we need to put it back later for performance reasons.
-
-class CPosPassThru : public IMediaSeeking, public CMediaPosition
-{
- IPin *m_pPin;
-
- HRESULT GetPeer(IMediaPosition **ppMP);
- HRESULT GetPeerSeeking(IMediaSeeking **ppMS);
-
-public:
-
- CPosPassThru(const TCHAR *, LPUNKNOWN, HRESULT*, IPin *);
- DECLARE_IUNKNOWN
-
- HRESULT ForceRefresh() {
- return S_OK;
- };
-
- // override to return an accurate current position
- virtual HRESULT GetMediaTime(LONGLONG *pStartTime,LONGLONG *pEndTime) {
- return E_FAIL;
- }
-
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv);
-
- // IMediaSeeking methods
- STDMETHODIMP GetCapabilities( DWORD * pCapabilities );
- STDMETHODIMP CheckCapabilities( DWORD * pCapabilities );
- STDMETHODIMP SetTimeFormat(const GUID * pFormat);
- STDMETHODIMP GetTimeFormat(GUID *pFormat);
- STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);
- STDMETHODIMP IsFormatSupported( const GUID * pFormat);
- STDMETHODIMP QueryPreferredFormat( GUID *pFormat);
- STDMETHODIMP ConvertTimeFormat(LONGLONG * pTarget, const GUID * pTargetFormat,
- LONGLONG Source, const GUID * pSourceFormat );
- STDMETHODIMP SetPositions( LONGLONG * pCurrent, DWORD CurrentFlags
- , LONGLONG * pStop, DWORD StopFlags );
-
- STDMETHODIMP GetPositions( LONGLONG * pCurrent, LONGLONG * pStop );
- STDMETHODIMP GetCurrentPosition( LONGLONG * pCurrent );
- STDMETHODIMP GetStopPosition( LONGLONG * pStop );
- STDMETHODIMP SetRate( double dRate);
- STDMETHODIMP GetRate( double * pdRate);
- STDMETHODIMP GetDuration( LONGLONG *pDuration);
- STDMETHODIMP GetAvailable( LONGLONG *pEarliest, LONGLONG *pLatest );
- STDMETHODIMP GetPreroll( LONGLONG *pllPreroll );
-
- // IMediaPosition properties
- STDMETHODIMP get_Duration(REFTIME * plength);
- STDMETHODIMP put_CurrentPosition(REFTIME llTime);
- STDMETHODIMP get_StopTime(REFTIME * pllTime);
- STDMETHODIMP put_StopTime(REFTIME llTime);
- STDMETHODIMP get_PrerollTime(REFTIME * pllTime);
- STDMETHODIMP put_PrerollTime(REFTIME llTime);
- STDMETHODIMP get_Rate(double * pdRate);
- STDMETHODIMP put_Rate(double dRate);
- STDMETHODIMP get_CurrentPosition(REFTIME * pllTime);
- STDMETHODIMP CanSeekForward(LONG *pCanSeekForward);
- STDMETHODIMP CanSeekBackward(LONG *pCanSeekBackward);
-
-private:
- HRESULT GetSeekingLongLong( HRESULT (__stdcall IMediaSeeking::*pMethod)( LONGLONG * ),
- LONGLONG * pll );
-};
-
-
-// Adds the ability to return a current position
-
-class CRendererPosPassThru : public CPosPassThru
-{
- CCritSec m_PositionLock; // Locks access to our position
- LONGLONG m_StartMedia; // Start media time last seen
- LONGLONG m_EndMedia; // And likewise the end media
- BOOL m_bReset; // Have media times been set
-
-public:
-
- // Used to help with passing media times through graph
-
- CRendererPosPassThru(const TCHAR *, LPUNKNOWN, HRESULT*, IPin *);
- HRESULT RegisterMediaTime(IMediaSample *pMediaSample);
- HRESULT RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime);
- HRESULT GetMediaTime(LONGLONG *pStartTime,LONGLONG *pEndTime);
- HRESULT ResetMediaTime();
- HRESULT EOS();
-};
-
-STDAPI CreatePosPassThru(
- LPUNKNOWN pAgg,
- BOOL bRenderer,
- IPin *pPin,
- IUnknown **ppPassThru
-);
-
-// A class that handles the IDispatch part of IBasicAudio and leaves the
-// properties and methods themselves pure virtual.
-
-class AM_NOVTABLE CBasicAudio : public IBasicAudio, public CUnknown
-{
- CBaseDispatch m_basedisp;
-
-public:
-
- CBasicAudio(const TCHAR *, LPUNKNOWN);
-
- DECLARE_IUNKNOWN
-
- // override this to publicise our interfaces
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- /* IDispatch methods */
- STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
-
- STDMETHODIMP GetTypeInfo(
- UINT itinfo,
- LCID lcid,
- ITypeInfo ** pptinfo);
-
- STDMETHODIMP GetIDsOfNames(
- REFIID riid,
- OLECHAR ** rgszNames,
- UINT cNames,
- LCID lcid,
- DISPID * rgdispid);
-
- STDMETHODIMP Invoke(
- DISPID dispidMember,
- REFIID riid,
- LCID lcid,
- WORD wFlags,
- DISPPARAMS * pdispparams,
- VARIANT * pvarResult,
- EXCEPINFO * pexcepinfo,
- UINT * puArgErr);
-};
-
-
-// A class that handles the IDispatch part of IBasicVideo and leaves the
-// properties and methods themselves pure virtual.
-
-class AM_NOVTABLE CBaseBasicVideo : public IBasicVideo2, public CUnknown
-{
- CBaseDispatch m_basedisp;
-
-public:
-
- CBaseBasicVideo(const TCHAR *, LPUNKNOWN);
-
- DECLARE_IUNKNOWN
-
- // override this to publicise our interfaces
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- /* IDispatch methods */
- STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
-
- STDMETHODIMP GetTypeInfo(
- UINT itinfo,
- LCID lcid,
- ITypeInfo ** pptinfo);
-
- STDMETHODIMP GetIDsOfNames(
- REFIID riid,
- OLECHAR ** rgszNames,
- UINT cNames,
- LCID lcid,
- DISPID * rgdispid);
-
- STDMETHODIMP Invoke(
- DISPID dispidMember,
- REFIID riid,
- LCID lcid,
- WORD wFlags,
- DISPPARAMS * pdispparams,
- VARIANT * pvarResult,
- EXCEPINFO * pexcepinfo,
- UINT * puArgErr);
-
- STDMETHODIMP GetPreferredAspectRatio(
- long *plAspectX,
- long *plAspectY)
- {
- return E_NOTIMPL;
- }
-};
-
-
-// A class that handles the IDispatch part of IVideoWindow and leaves the
-// properties and methods themselves pure virtual.
-
-class AM_NOVTABLE CBaseVideoWindow : public IVideoWindow, public CUnknown
-{
- CBaseDispatch m_basedisp;
-
-public:
-
- CBaseVideoWindow(const TCHAR *, LPUNKNOWN);
-
- DECLARE_IUNKNOWN
-
- // override this to publicise our interfaces
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- /* IDispatch methods */
- STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
-
- STDMETHODIMP GetTypeInfo(
- UINT itinfo,
- LCID lcid,
- ITypeInfo ** pptinfo);
-
- STDMETHODIMP GetIDsOfNames(
- REFIID riid,
- OLECHAR ** rgszNames,
- UINT cNames,
- LCID lcid,
- DISPID * rgdispid);
-
- STDMETHODIMP Invoke(
- DISPID dispidMember,
- REFIID riid,
- LCID lcid,
- WORD wFlags,
- DISPPARAMS * pdispparams,
- VARIANT * pvarResult,
- EXCEPINFO * pexcepinfo,
- UINT * puArgErr);
-};
-
-
-// abstract class to help source filters with their implementation
-// of IMediaPosition. Derive from this and set the duration (and stop
-// position). Also override NotifyChange to do something when the properties
-// change.
-
-class AM_NOVTABLE CSourcePosition : public CMediaPosition
-{
-
-public:
- CSourcePosition(const TCHAR *, LPUNKNOWN, HRESULT*, CCritSec *);
-
- // IMediaPosition methods
- STDMETHODIMP get_Duration(REFTIME * plength);
- STDMETHODIMP put_CurrentPosition(REFTIME llTime);
- STDMETHODIMP get_StopTime(REFTIME * pllTime);
- STDMETHODIMP put_StopTime(REFTIME llTime);
- STDMETHODIMP get_PrerollTime(REFTIME * pllTime);
- STDMETHODIMP put_PrerollTime(REFTIME llTime);
- STDMETHODIMP get_Rate(double * pdRate);
- STDMETHODIMP put_Rate(double dRate);
- STDMETHODIMP CanSeekForward(LONG *pCanSeekForward);
- STDMETHODIMP CanSeekBackward(LONG *pCanSeekBackward);
-
- // override if you can return the data you are actually working on
- STDMETHODIMP get_CurrentPosition(REFTIME * pllTime) {
- return E_NOTIMPL;
- };
-
-protected:
-
- // we call this to notify changes. Override to handle them
- virtual HRESULT ChangeStart() PURE;
- virtual HRESULT ChangeStop() PURE;
- virtual HRESULT ChangeRate() PURE;
-
- COARefTime m_Duration;
- COARefTime m_Start;
- COARefTime m_Stop;
- double m_Rate;
-
- CCritSec * m_pLock;
-};
-
-class AM_NOVTABLE CSourceSeeking :
- public IMediaSeeking,
- public CUnknown
-{
-
-public:
-
- DECLARE_IUNKNOWN;
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- // IMediaSeeking methods
-
- STDMETHODIMP IsFormatSupported(const GUID * pFormat);
- STDMETHODIMP QueryPreferredFormat(GUID *pFormat);
- STDMETHODIMP SetTimeFormat(const GUID * pFormat);
- STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);
- STDMETHODIMP GetTimeFormat(GUID *pFormat);
- STDMETHODIMP GetDuration(LONGLONG *pDuration);
- STDMETHODIMP GetStopPosition(LONGLONG *pStop);
- STDMETHODIMP GetCurrentPosition(LONGLONG *pCurrent);
- STDMETHODIMP GetCapabilities( DWORD * pCapabilities );
- STDMETHODIMP CheckCapabilities( DWORD * pCapabilities );
- STDMETHODIMP ConvertTimeFormat( LONGLONG * pTarget, const GUID * pTargetFormat,
- LONGLONG Source, const GUID * pSourceFormat );
-
- STDMETHODIMP SetPositions( LONGLONG * pCurrent, DWORD CurrentFlags
- , LONGLONG * pStop, DWORD StopFlags );
-
- STDMETHODIMP GetPositions( LONGLONG * pCurrent, LONGLONG * pStop );
-
- STDMETHODIMP GetAvailable( LONGLONG * pEarliest, LONGLONG * pLatest );
- STDMETHODIMP SetRate( double dRate);
- STDMETHODIMP GetRate( double * pdRate);
- STDMETHODIMP GetPreroll(LONGLONG *pPreroll);
-
-
-protected:
-
- // ctor
- CSourceSeeking(const TCHAR *, LPUNKNOWN, HRESULT*, CCritSec *);
-
- // we call this to notify changes. Override to handle them
- virtual HRESULT ChangeStart() PURE;
- virtual HRESULT ChangeStop() PURE;
- virtual HRESULT ChangeRate() PURE;
-
- CRefTime m_rtDuration; // length of stream
- CRefTime m_rtStart; // source will start here
- CRefTime m_rtStop; // source will stop here
- double m_dRateSeeking;
-
- // seeking capabilities
- DWORD m_dwSeekingCaps;
-
- CCritSec * m_pLock;
-};
-
-
-// Base classes supporting Deferred commands.
-
-// Deferred commands are queued by calls to methods on the IQueueCommand
-// interface, exposed by the filtergraph and by some filters. A successful
-// call to one of these methods will return an IDeferredCommand interface
-// representing the queued command.
-//
-// A CDeferredCommand object represents a single deferred command, and exposes
-// the IDeferredCommand interface as well as other methods permitting time
-// checks and actual execution. It contains a reference to the CCommandQueue
-// object on which it is queued.
-//
-// CCommandQueue is a base class providing a queue of CDeferredCommand
-// objects, and methods to add, remove, check status and invoke the queued
-// commands. A CCommandQueue object would be part of an object that
-// implemented IQueueCommand.
-
-class CCmdQueue;
-
-// take a copy of the params and store them. Release any allocated
-// memory in destructor
-
-class CDispParams : public DISPPARAMS
-{
-public:
- CDispParams(UINT nArgs, VARIANT* pArgs, HRESULT *phr = NULL);
- ~CDispParams();
-};
-
-
-// CDeferredCommand lifetime is controlled by refcounts. Caller of
-// InvokeAt.. gets a refcounted interface pointer, and the CCmdQueue
-// object also holds a refcount on us. Calling Cancel or Invoke takes
-// us off the CCmdQueue and thus reduces the refcount by 1. Once taken
-// off the queue we cannot be put back on the queue.
-
-class CDeferredCommand
- : public CUnknown,
- public IDeferredCommand
-{
-public:
-
- CDeferredCommand(
- CCmdQueue * pQ,
- LPUNKNOWN pUnk, // aggregation outer unk
- HRESULT * phr,
- LPUNKNOWN pUnkExecutor, // object that will execute this cmd
- REFTIME time,
- GUID* iid,
- long dispidMethod,
- short wFlags,
- long cArgs,
- VARIANT* pDispParams,
- VARIANT* pvarResult,
- short* puArgErr,
- BOOL bStream
- );
-
- DECLARE_IUNKNOWN
-
- // override this to publicise our interfaces
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- // IDeferredCommand methods
- STDMETHODIMP Cancel();
- STDMETHODIMP Confidence(
- LONG* pConfidence);
- STDMETHODIMP Postpone(
- REFTIME newtime);
- STDMETHODIMP GetHResult(
- HRESULT* phrResult);
-
- // other public methods
-
- HRESULT Invoke();
-
- // access methods
-
- // returns TRUE if streamtime, FALSE if presentation time
- BOOL IsStreamTime() {
- return m_bStream;
- };
-
- CRefTime GetTime() {
- return m_time;
- };
-
- REFIID GetIID() {
- return *m_iid;
- };
-
- long GetMethod() {
- return m_dispidMethod;
- };
-
- short GetFlags() {
- return m_wFlags;
- };
-
- DISPPARAMS* GetParams() {
- return &m_DispParams;
- };
-
- VARIANT* GetResult() {
- return m_pvarResult;
- };
-
-protected:
-
- CCmdQueue* m_pQueue;
-
- // pUnk for the interface that we will execute the command on
- LPUNKNOWN m_pUnk;
-
- // stored command data
- REFERENCE_TIME m_time;
- GUID* m_iid;
- long m_dispidMethod;
- short m_wFlags;
- VARIANT* m_pvarResult;
- BOOL m_bStream;
- CDispParams m_DispParams;
- DISPID m_DispId; // For get and put
-
- // we use this for ITypeInfo access
- CBaseDispatch m_Dispatch;
-
- // save retval here
- HRESULT m_hrResult;
-};
-
-
-// a list of CDeferredCommand objects. this is a base class providing
-// the basics of access to the list. If you want to use CDeferredCommand
-// objects then your queue needs to be derived from this class.
-
-class AM_NOVTABLE CCmdQueue
-{
-public:
- CCmdQueue();
- virtual ~CCmdQueue();
-
- // returns a new CDeferredCommand object that will be initialised with
- // the parameters and will be added to the queue during construction.
- // returns S_OK if successfully created otherwise an error and
- // no object has been queued.
- virtual HRESULT New(
- CDeferredCommand **ppCmd,
- LPUNKNOWN pUnk,
- REFTIME time,
- GUID* iid,
- long dispidMethod,
- short wFlags,
- long cArgs,
- VARIANT* pDispParams,
- VARIANT* pvarResult,
- short* puArgErr,
- BOOL bStream
- );
-
- // called by the CDeferredCommand object to add and remove itself
- // from the queue
- virtual HRESULT Insert(CDeferredCommand* pCmd);
- virtual HRESULT Remove(CDeferredCommand* pCmd);
-
- // Command-Due Checking
- //
- // There are two schemes of synchronisation: coarse and accurate. In
- // coarse mode, you wait till the time arrives and then execute the cmd.
- // In accurate mode, you wait until you are processing the sample that
- // will appear at the time, and then execute the command. It's up to the
- // filter which one it will implement. The filtergraph will always
- // implement coarse mode for commands queued at the filtergraph.
- //
- // If you want coarse sync, you probably want to wait until there is a
- // command due, and then execute it. You can do this by calling
- // GetDueCommand. If you have several things to wait for, get the
- // event handle from GetDueHandle() and when this is signalled then call
- // GetDueCommand. Stream time will only advance between calls to Run and
- // EndRun. Note that to avoid an extra thread there is no guarantee that
- // if the handle is set there will be a command ready. Each time the
- // event is signalled, call GetDueCommand (probably with a 0 timeout);
- // This may return E_ABORT.
- //
- // If you want accurate sync, you must call GetCommandDueFor, passing
- // as a parameter the stream time of the samples you are about to process.
- // This will return:
- // -- a stream-time command due at or before that stream time
- // -- a presentation-time command due at or before the
- // time that stream time will be presented (only between Run
- // and EndRun calls, since outside of this, the mapping from
- // stream time to presentation time is not known.
- // -- any presentation-time command due now.
- // This means that if you want accurate synchronisation on samples that
- // might be processed during Paused mode, you need to use
- // stream-time commands.
- //
- // In all cases, commands remain queued until Invoked or Cancelled. The
- // setting and resetting of the event handle is managed entirely by this
- // queue object.
-
- // set the clock used for timing
- virtual HRESULT SetSyncSource(IReferenceClock*);
-
- // switch to run mode. Streamtime to Presentation time mapping known.
- virtual HRESULT Run(REFERENCE_TIME tStreamTimeOffset);
-
- // switch to Stopped or Paused mode. Time mapping not known.
- virtual HRESULT EndRun();
-
- // return a pointer to the next due command. Blocks for msTimeout
- // milliseconds until there is a due command.
- // Stream-time commands will only become due between Run and Endrun calls.
- // The command remains queued until invoked or cancelled.
- // Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).
- // Returns an AddRef-ed object
- virtual HRESULT GetDueCommand(CDeferredCommand ** ppCmd, long msTimeout);
-
- // return the event handle that will be signalled whenever
- // there are deferred commands due for execution (when GetDueCommand
- // will not block).
- HANDLE GetDueHandle() {
- return HANDLE(m_evDue);
- };
-
- // return a pointer to a command that will be due for a given time.
- // Pass in a stream time here. The stream time offset will be passed
- // in via the Run method.
- // Commands remain queued until invoked or cancelled.
- // This method will not block. It will report VFW_E_NOT_FOUND if there
- // are no commands due yet.
- // Returns an AddRef-ed object
- virtual HRESULT GetCommandDueFor(REFERENCE_TIME tStream, CDeferredCommand**ppCmd);
-
- // check if a given time is due (TRUE if it is due yet)
- BOOL CheckTime(CRefTime time, BOOL bStream) {
-
- // if no clock, nothing is due!
- if (!m_pClock) {
- return FALSE;
- }
-
- // stream time
- if (bStream) {
-
- // not valid if not running
- if (!m_bRunning) {
- return FALSE;
- }
- // add on known stream time offset to get presentation time
- time += m_StreamTimeOffset;
- }
-
- CRefTime Now;
- m_pClock->GetTime((REFERENCE_TIME*)&Now);
- return (time <= Now);
- };
-
-protected:
-
- // protect access to lists etc
- CCritSec m_Lock;
-
- // commands queued in presentation time are stored here
- CGenericList m_listPresentation;
-
- // commands queued in stream time are stored here
- CGenericList m_listStream;
-
- // set when any commands are due
- CAMEvent m_evDue;
-
- // creates an advise for the earliest time required, if any
- void SetTimeAdvise(void);
-
- // advise id from reference clock (0 if no outstanding advise)
- DWORD_PTR m_dwAdvise;
-
- // advise time is for this presentation time
- CRefTime m_tCurrentAdvise;
-
- // the reference clock we are using (addrefed)
- IReferenceClock* m_pClock;
-
- // true when running
- BOOL m_bRunning;
-
- // contains stream time offset when m_bRunning is true
- CRefTime m_StreamTimeOffset;
-};
-
-#endif // __CTLUTIL__
+//------------------------------------------------------------------------------
+// File: CtlUtil.h
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+// Base classes implementing IDispatch parsing for the basic control dual
+// interfaces. Derive from these and implement just the custom method and
+// property methods. We also implement CPosPassThru that can be used by
+// renderers and transforms to pass by IMediaPosition and IMediaSeeking
+
+#ifndef __CTLUTIL__
+#define __CTLUTIL__
+
+// OLE Automation has different ideas of TRUE and FALSE
+
+#define OATRUE (-1)
+#define OAFALSE (0)
+
+
+// It's possible that we could replace this class with CreateStdDispatch
+
+class CBaseDispatch
+{
+ ITypeInfo * m_pti;
+
+public:
+
+ CBaseDispatch() : m_pti(NULL) {}
+ ~CBaseDispatch();
+
+ /* IDispatch methods */
+ STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
+
+ STDMETHODIMP GetTypeInfo(
+ REFIID riid,
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo);
+
+ STDMETHODIMP GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid);
+};
+
+
+class AM_NOVTABLE CMediaControl :
+ public IMediaControl,
+ public CUnknown
+{
+ CBaseDispatch m_basedisp;
+
+public:
+
+ CMediaControl(const TCHAR *, LPUNKNOWN);
+
+ DECLARE_IUNKNOWN
+
+ // override this to publicise our interfaces
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+ /* IDispatch methods */
+ STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
+
+ STDMETHODIMP GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo);
+
+ STDMETHODIMP GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid);
+
+ STDMETHODIMP Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr);
+};
+
+
+class AM_NOVTABLE CMediaEvent :
+ public IMediaEventEx,
+ public CUnknown
+{
+ CBaseDispatch m_basedisp;
+
+public:
+
+ CMediaEvent(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
+
+ DECLARE_IUNKNOWN
+
+ // override this to publicise our interfaces
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+ /* IDispatch methods */
+ STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
+
+ STDMETHODIMP GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo);
+
+ STDMETHODIMP GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid);
+
+ STDMETHODIMP Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr);
+};
+
+
+class AM_NOVTABLE CMediaPosition :
+ public IMediaPosition,
+ public CUnknown
+{
+ CBaseDispatch m_basedisp;
+
+
+public:
+
+ CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
+ CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT *phr);
+
+ DECLARE_IUNKNOWN
+
+ // override this to publicise our interfaces
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+ /* IDispatch methods */
+ STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
+
+ STDMETHODIMP GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo);
+
+ STDMETHODIMP GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid);
+
+ STDMETHODIMP Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr);
+
+};
+
+
+// OA-compatibility means that we must use double as the RefTime value,
+// and REFERENCE_TIME (essentially a LONGLONG) within filters.
+// this class converts between the two
+
+class COARefTime : public CRefTime {
+public:
+
+ COARefTime() {
+ };
+
+ COARefTime(CRefTime t)
+ : CRefTime(t)
+ {
+ };
+
+ COARefTime(REFERENCE_TIME t)
+ : CRefTime(t)
+ {
+ };
+
+ COARefTime(double d) {
+ m_time = (LONGLONG) (d * 10000000);
+ };
+
+ operator double() {
+ return double(m_time) / 10000000;
+ };
+
+ operator REFERENCE_TIME() {
+ return m_time;
+ };
+
+ COARefTime& operator=(const double& rd) {
+ m_time = (LONGLONG) (rd * 10000000);
+ return *this;
+ }
+
+ COARefTime& operator=(const REFERENCE_TIME& rt) {
+ m_time = rt;
+ return *this;
+ }
+
+ inline BOOL operator==(const COARefTime& rt)
+ {
+ return m_time == rt.m_time;
+ };
+
+ inline BOOL operator!=(const COARefTime& rt)
+ {
+ return m_time != rt.m_time;
+ };
+
+ inline BOOL operator < (const COARefTime& rt)
+ {
+ return m_time < rt.m_time;
+ };
+
+ inline BOOL operator > (const COARefTime& rt)
+ {
+ return m_time > rt.m_time;
+ };
+
+ inline BOOL operator >= (const COARefTime& rt)
+ {
+ return m_time >= rt.m_time;
+ };
+
+ inline BOOL operator <= (const COARefTime& rt)
+ {
+ return m_time <= rt.m_time;
+ };
+
+ inline COARefTime operator+(const COARefTime& rt)
+ {
+ return COARefTime(m_time + rt.m_time);
+ };
+
+ inline COARefTime operator-(const COARefTime& rt)
+ {
+ return COARefTime(m_time - rt.m_time);
+ };
+
+ inline COARefTime operator*(LONG l)
+ {
+ return COARefTime(m_time * l);
+ };
+
+ inline COARefTime operator/(LONG l)
+ {
+ return COARefTime(m_time / l);
+ };
+
+private:
+ // Prevent bugs from constructing from LONG (which gets
+ // converted to double and then multiplied by 10000000
+ COARefTime(LONG);
+ LONG operator=(LONG);
+};
+
+
+// A utility class that handles IMediaPosition and IMediaSeeking on behalf
+// of single-input pin renderers, or transform filters.
+//
+// Renderers will expose this from the filter; transform filters will
+// expose it from the output pin and not the renderer.
+//
+// Create one of these, giving it your IPin* for your input pin, and delegate
+// all IMediaPosition methods to it. It will query the input pin for
+// IMediaPosition and respond appropriately.
+//
+// Call ForceRefresh if the pin connection changes.
+//
+// This class no longer caches the upstream IMediaPosition or IMediaSeeking
+// it acquires it on each method call. This means ForceRefresh is not needed.
+// The method is kept for source compatibility and to minimise the changes
+// if we need to put it back later for performance reasons.
+
+class CPosPassThru : public IMediaSeeking, public CMediaPosition
+{
+ IPin *m_pPin;
+
+ HRESULT GetPeer(__deref_out IMediaPosition **ppMP);
+ HRESULT GetPeerSeeking(__deref_out IMediaSeeking **ppMS);
+
+public:
+
+ CPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *);
+ DECLARE_IUNKNOWN
+
+ HRESULT ForceRefresh() {
+ return S_OK;
+ };
+
+ // override to return an accurate current position
+ virtual HRESULT GetMediaTime(__out LONGLONG *pStartTime, __out_opt LONGLONG *pEndTime) {
+ return E_FAIL;
+ }
+
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv);
+
+ // IMediaSeeking methods
+ STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities );
+ STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities );
+ STDMETHODIMP SetTimeFormat(const GUID * pFormat);
+ STDMETHODIMP GetTimeFormat(__out GUID *pFormat);
+ STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);
+ STDMETHODIMP IsFormatSupported( const GUID * pFormat);
+ STDMETHODIMP QueryPreferredFormat( __out GUID *pFormat);
+ STDMETHODIMP ConvertTimeFormat(__out LONGLONG * pTarget,
+ __in_opt const GUID * pTargetFormat,
+ LONGLONG Source,
+ __in_opt const GUID * pSourceFormat );
+ STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent, DWORD CurrentFlags
+ , __inout_opt LONGLONG * pStop, DWORD StopFlags );
+
+ STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop );
+ STDMETHODIMP GetCurrentPosition( __out LONGLONG * pCurrent );
+ STDMETHODIMP GetStopPosition( __out LONGLONG * pStop );
+ STDMETHODIMP SetRate( double dRate);
+ STDMETHODIMP GetRate( __out double * pdRate);
+ STDMETHODIMP GetDuration( __out LONGLONG *pDuration);
+ STDMETHODIMP GetAvailable( __out_opt LONGLONG *pEarliest, __out_opt LONGLONG *pLatest );
+ STDMETHODIMP GetPreroll( __out LONGLONG *pllPreroll );
+
+ // IMediaPosition properties
+ STDMETHODIMP get_Duration(__out REFTIME * plength);
+ STDMETHODIMP put_CurrentPosition(REFTIME llTime);
+ STDMETHODIMP get_StopTime(__out REFTIME * pllTime);
+ STDMETHODIMP put_StopTime(REFTIME llTime);
+ STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime);
+ STDMETHODIMP put_PrerollTime(REFTIME llTime);
+ STDMETHODIMP get_Rate(__out double * pdRate);
+ STDMETHODIMP put_Rate(double dRate);
+ STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime);
+ STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward);
+ STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward);
+
+private:
+ HRESULT GetSeekingLongLong( HRESULT (__stdcall IMediaSeeking::*pMethod)( LONGLONG * ),
+ __out LONGLONG * pll );
+};
+
+
+// Adds the ability to return a current position
+
+class CRendererPosPassThru : public CPosPassThru
+{
+ CCritSec m_PositionLock; // Locks access to our position
+ LONGLONG m_StartMedia; // Start media time last seen
+ LONGLONG m_EndMedia; // And likewise the end media
+ BOOL m_bReset; // Have media times been set
+
+public:
+
+ // Used to help with passing media times through graph
+
+ CRendererPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *);
+ HRESULT RegisterMediaTime(IMediaSample *pMediaSample);
+ HRESULT RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime);
+ HRESULT GetMediaTime(__out LONGLONG *pStartTime,__out_opt LONGLONG *pEndTime);
+ HRESULT ResetMediaTime();
+ HRESULT EOS();
+};
+
+STDAPI CreatePosPassThru(
+ __in_opt LPUNKNOWN pAgg,
+ BOOL bRenderer,
+ IPin *pPin,
+ __deref_out IUnknown **ppPassThru
+);
+
+// A class that handles the IDispatch part of IBasicAudio and leaves the
+// properties and methods themselves pure virtual.
+
+class AM_NOVTABLE CBasicAudio : public IBasicAudio, public CUnknown
+{
+ CBaseDispatch m_basedisp;
+
+public:
+
+ CBasicAudio(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
+
+ DECLARE_IUNKNOWN
+
+ // override this to publicise our interfaces
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+ /* IDispatch methods */
+ STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
+
+ STDMETHODIMP GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo);
+
+ STDMETHODIMP GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid);
+
+ STDMETHODIMP Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr);
+};
+
+
+// A class that handles the IDispatch part of IBasicVideo and leaves the
+// properties and methods themselves pure virtual.
+
+class AM_NOVTABLE CBaseBasicVideo : public IBasicVideo2, public CUnknown
+{
+ CBaseDispatch m_basedisp;
+
+public:
+
+ CBaseBasicVideo(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
+
+ DECLARE_IUNKNOWN
+
+ // override this to publicise our interfaces
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+ /* IDispatch methods */
+ STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
+
+ STDMETHODIMP GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo);
+
+ STDMETHODIMP GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid);
+
+ STDMETHODIMP Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr);
+
+ STDMETHODIMP GetPreferredAspectRatio(
+ __out long *plAspectX,
+ __out long *plAspectY)
+ {
+ return E_NOTIMPL;
+ }
+};
+
+
+// A class that handles the IDispatch part of IVideoWindow and leaves the
+// properties and methods themselves pure virtual.
+
+class AM_NOVTABLE CBaseVideoWindow : public IVideoWindow, public CUnknown
+{
+ CBaseDispatch m_basedisp;
+
+public:
+
+ CBaseVideoWindow(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
+
+ DECLARE_IUNKNOWN
+
+ // override this to publicise our interfaces
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+ /* IDispatch methods */
+ STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
+
+ STDMETHODIMP GetTypeInfo(
+ UINT itinfo,
+ LCID lcid,
+ __deref_out ITypeInfo ** pptinfo);
+
+ STDMETHODIMP GetIDsOfNames(
+ REFIID riid,
+ __in_ecount(cNames) LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID lcid,
+ __out_ecount(cNames) DISPID * rgdispid);
+
+ STDMETHODIMP Invoke(
+ DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ __in DISPPARAMS * pdispparams,
+ __out_opt VARIANT * pvarResult,
+ __out_opt EXCEPINFO * pexcepinfo,
+ __out_opt UINT * puArgErr);
+};
+
+
+// abstract class to help source filters with their implementation
+// of IMediaPosition. Derive from this and set the duration (and stop
+// position). Also override NotifyChange to do something when the properties
+// change.
+
+class AM_NOVTABLE CSourcePosition : public CMediaPosition
+{
+
+public:
+ CSourcePosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *);
+
+ // IMediaPosition methods
+ STDMETHODIMP get_Duration(__out REFTIME * plength);
+ STDMETHODIMP put_CurrentPosition(REFTIME llTime);
+ STDMETHODIMP get_StopTime(__out REFTIME * pllTime);
+ STDMETHODIMP put_StopTime(REFTIME llTime);
+ STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime);
+ STDMETHODIMP put_PrerollTime(REFTIME llTime);
+ STDMETHODIMP get_Rate(__out double * pdRate);
+ STDMETHODIMP put_Rate(double dRate);
+ STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward);
+ STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward);
+
+ // override if you can return the data you are actually working on
+ STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime) {
+ return E_NOTIMPL;
+ };
+
+protected:
+
+ // we call this to notify changes. Override to handle them
+ virtual HRESULT ChangeStart() PURE;
+ virtual HRESULT ChangeStop() PURE;
+ virtual HRESULT ChangeRate() PURE;
+
+ COARefTime m_Duration;
+ COARefTime m_Start;
+ COARefTime m_Stop;
+ double m_Rate;
+
+ CCritSec * m_pLock;
+};
+
+class AM_NOVTABLE CSourceSeeking :
+ public IMediaSeeking,
+ public CUnknown
+{
+
+public:
+
+ DECLARE_IUNKNOWN;
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+ // IMediaSeeking methods
+
+ STDMETHODIMP IsFormatSupported(const GUID * pFormat);
+ STDMETHODIMP QueryPreferredFormat(__out GUID *pFormat);
+ STDMETHODIMP SetTimeFormat(const GUID * pFormat);
+ STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);
+ STDMETHODIMP GetTimeFormat(__out GUID *pFormat);
+ STDMETHODIMP GetDuration(__out LONGLONG *pDuration);
+ STDMETHODIMP GetStopPosition(__out LONGLONG *pStop);
+ STDMETHODIMP GetCurrentPosition(__out LONGLONG *pCurrent);
+ STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities );
+ STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities );
+ STDMETHODIMP ConvertTimeFormat( __out LONGLONG * pTarget,
+ __in_opt const GUID * pTargetFormat,
+ LONGLONG Source,
+ __in_opt const GUID * pSourceFormat );
+
+ STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent, DWORD CurrentFlags
+ , __inout_opt LONGLONG * pStop, DWORD StopFlags );
+
+ STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop );
+
+ STDMETHODIMP GetAvailable( __out_opt LONGLONG * pEarliest, __out_opt LONGLONG * pLatest );
+ STDMETHODIMP SetRate( double dRate);
+ STDMETHODIMP GetRate( __out double * pdRate);
+ STDMETHODIMP GetPreroll(__out LONGLONG *pPreroll);
+
+
+protected:
+
+ // ctor
+ CSourceSeeking(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *);
+
+ // we call this to notify changes. Override to handle them
+ virtual HRESULT ChangeStart() PURE;
+ virtual HRESULT ChangeStop() PURE;
+ virtual HRESULT ChangeRate() PURE;
+
+ CRefTime m_rtDuration; // length of stream
+ CRefTime m_rtStart; // source will start here
+ CRefTime m_rtStop; // source will stop here
+ double m_dRateSeeking;
+
+ // seeking capabilities
+ DWORD m_dwSeekingCaps;
+
+ CCritSec * m_pLock;
+};
+
+
+// Base classes supporting Deferred commands.
+
+// Deferred commands are queued by calls to methods on the IQueueCommand
+// interface, exposed by the filtergraph and by some filters. A successful
+// call to one of these methods will return an IDeferredCommand interface
+// representing the queued command.
+//
+// A CDeferredCommand object represents a single deferred command, and exposes
+// the IDeferredCommand interface as well as other methods permitting time
+// checks and actual execution. It contains a reference to the CCommandQueue
+// object on which it is queued.
+//
+// CCommandQueue is a base class providing a queue of CDeferredCommand
+// objects, and methods to add, remove, check status and invoke the queued
+// commands. A CCommandQueue object would be part of an object that
+// implemented IQueueCommand.
+
+class CCmdQueue;
+
+// take a copy of the params and store them. Release any allocated
+// memory in destructor
+
+class CDispParams : public DISPPARAMS
+{
+public:
+ CDispParams(UINT nArgs, __in_ecount(nArgs) VARIANT* pArgs, __inout_opt HRESULT *phr = NULL);
+ ~CDispParams();
+};
+
+
+// CDeferredCommand lifetime is controlled by refcounts. Caller of
+// InvokeAt.. gets a refcounted interface pointer, and the CCmdQueue
+// object also holds a refcount on us. Calling Cancel or Invoke takes
+// us off the CCmdQueue and thus reduces the refcount by 1. Once taken
+// off the queue we cannot be put back on the queue.
+
+class CDeferredCommand
+ : public CUnknown,
+ public IDeferredCommand
+{
+public:
+
+ CDeferredCommand(
+ __inout CCmdQueue * pQ,
+ __in_opt LPUNKNOWN pUnk, // aggregation outer unk
+ __inout HRESULT * phr,
+ __in LPUNKNOWN pUnkExecutor, // object that will execute this cmd
+ REFTIME time,
+ __in GUID* iid,
+ long dispidMethod,
+ short wFlags,
+ long cArgs,
+ __in_ecount(cArgs) VARIANT* pDispParams,
+ __out VARIANT* pvarResult,
+ __out short* puArgErr,
+ BOOL bStream
+ );
+
+ DECLARE_IUNKNOWN
+
+ // override this to publicise our interfaces
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __out void **ppv);
+
+ // IDeferredCommand methods
+ STDMETHODIMP Cancel();
+ STDMETHODIMP Confidence(
+ __out LONG* pConfidence);
+ STDMETHODIMP Postpone(
+ REFTIME newtime);
+ STDMETHODIMP GetHResult(
+ __out HRESULT* phrResult);
+
+ // other public methods
+
+ HRESULT Invoke();
+
+ // access methods
+
+ // returns TRUE if streamtime, FALSE if presentation time
+ BOOL IsStreamTime() {
+ return m_bStream;
+ };
+
+ CRefTime GetTime() {
+ return m_time;
+ };
+
+ REFIID GetIID() {
+ return *m_iid;
+ };
+
+ long GetMethod() {
+ return m_dispidMethod;
+ };
+
+ short GetFlags() {
+ return m_wFlags;
+ };
+
+ DISPPARAMS* GetParams() {
+ return &m_DispParams;
+ };
+
+ VARIANT* GetResult() {
+ return m_pvarResult;
+ };
+
+protected:
+
+ CCmdQueue* m_pQueue;
+
+ // pUnk for the interface that we will execute the command on
+ LPUNKNOWN m_pUnk;
+
+ // stored command data
+ REFERENCE_TIME m_time;
+ GUID* m_iid;
+ long m_dispidMethod;
+ short m_wFlags;
+ VARIANT* m_pvarResult;
+ BOOL m_bStream;
+ CDispParams m_DispParams;
+ DISPID m_DispId; // For get and put
+
+ // we use this for ITypeInfo access
+ CBaseDispatch m_Dispatch;
+
+ // save retval here
+ HRESULT m_hrResult;
+};
+
+
+// a list of CDeferredCommand objects. this is a base class providing
+// the basics of access to the list. If you want to use CDeferredCommand
+// objects then your queue needs to be derived from this class.
+
+class AM_NOVTABLE CCmdQueue
+{
+public:
+ CCmdQueue(__inout_opt HRESULT *phr = NULL);
+ virtual ~CCmdQueue();
+
+ // returns a new CDeferredCommand object that will be initialised with
+ // the parameters and will be added to the queue during construction.
+ // returns S_OK if successfully created otherwise an error and
+ // no object has been queued.
+ virtual HRESULT New(
+ __out CDeferredCommand **ppCmd,
+ __in LPUNKNOWN pUnk,
+ REFTIME time,
+ __in GUID* iid,
+ long dispidMethod,
+ short wFlags,
+ long cArgs,
+ __in_ecount(cArgs) VARIANT* pDispParams,
+ __out VARIANT* pvarResult,
+ __out short* puArgErr,
+ BOOL bStream
+ );
+
+ // called by the CDeferredCommand object to add and remove itself
+ // from the queue
+ virtual HRESULT Insert(__in CDeferredCommand* pCmd);
+ virtual HRESULT Remove(__in CDeferredCommand* pCmd);
+
+ // Command-Due Checking
+ //
+ // There are two schemes of synchronisation: coarse and accurate. In
+ // coarse mode, you wait till the time arrives and then execute the cmd.
+ // In accurate mode, you wait until you are processing the sample that
+ // will appear at the time, and then execute the command. It's up to the
+ // filter which one it will implement. The filtergraph will always
+ // implement coarse mode for commands queued at the filtergraph.
+ //
+ // If you want coarse sync, you probably want to wait until there is a
+ // command due, and then execute it. You can do this by calling
+ // GetDueCommand. If you have several things to wait for, get the
+ // event handle from GetDueHandle() and when this is signalled then call
+ // GetDueCommand. Stream time will only advance between calls to Run and
+ // EndRun. Note that to avoid an extra thread there is no guarantee that
+ // if the handle is set there will be a command ready. Each time the
+ // event is signalled, call GetDueCommand (probably with a 0 timeout);
+ // This may return E_ABORT.
+ //
+ // If you want accurate sync, you must call GetCommandDueFor, passing
+ // as a parameter the stream time of the samples you are about to process.
+ // This will return:
+ // -- a stream-time command due at or before that stream time
+ // -- a presentation-time command due at or before the
+ // time that stream time will be presented (only between Run
+ // and EndRun calls, since outside of this, the mapping from
+ // stream time to presentation time is not known.
+ // -- any presentation-time command due now.
+ // This means that if you want accurate synchronisation on samples that
+ // might be processed during Paused mode, you need to use
+ // stream-time commands.
+ //
+ // In all cases, commands remain queued until Invoked or Cancelled. The
+ // setting and resetting of the event handle is managed entirely by this
+ // queue object.
+
+ // set the clock used for timing
+ virtual HRESULT SetSyncSource(__in_opt IReferenceClock*);
+
+ // switch to run mode. Streamtime to Presentation time mapping known.
+ virtual HRESULT Run(REFERENCE_TIME tStreamTimeOffset);
+
+ // switch to Stopped or Paused mode. Time mapping not known.
+ virtual HRESULT EndRun();
+
+ // return a pointer to the next due command. Blocks for msTimeout
+ // milliseconds until there is a due command.
+ // Stream-time commands will only become due between Run and Endrun calls.
+ // The command remains queued until invoked or cancelled.
+ // Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).
+ // Returns an AddRef-ed object
+ virtual HRESULT GetDueCommand(__out CDeferredCommand ** ppCmd, long msTimeout);
+
+ // return the event handle that will be signalled whenever
+ // there are deferred commands due for execution (when GetDueCommand
+ // will not block).
+ HANDLE GetDueHandle() {
+ return HANDLE(m_evDue);
+ };
+
+ // return a pointer to a command that will be due for a given time.
+ // Pass in a stream time here. The stream time offset will be passed
+ // in via the Run method.
+ // Commands remain queued until invoked or cancelled.
+ // This method will not block. It will report VFW_E_NOT_FOUND if there
+ // are no commands due yet.
+ // Returns an AddRef-ed object
+ virtual HRESULT GetCommandDueFor(REFERENCE_TIME tStream, __out CDeferredCommand**ppCmd);
+
+ // check if a given time is due (TRUE if it is due yet)
+ BOOL CheckTime(CRefTime time, BOOL bStream) {
+
+ // if no clock, nothing is due!
+ if (!m_pClock) {
+ return FALSE;
+ }
+
+ // stream time
+ if (bStream) {
+
+ // not valid if not running
+ if (!m_bRunning) {
+ return FALSE;
+ }
+ // add on known stream time offset to get presentation time
+ time += m_StreamTimeOffset;
+ }
+
+ CRefTime Now;
+ m_pClock->GetTime((REFERENCE_TIME*)&Now);
+ return (time <= Now);
+ };
+
+protected:
+
+ // protect access to lists etc
+ CCritSec m_Lock;
+
+ // commands queued in presentation time are stored here
+ CGenericList m_listPresentation;
+
+ // commands queued in stream time are stored here
+ CGenericList m_listStream;
+
+ // set when any commands are due
+ CAMEvent m_evDue;
+
+ // creates an advise for the earliest time required, if any
+ void SetTimeAdvise(void);
+
+ // advise id from reference clock (0 if no outstanding advise)
+ DWORD_PTR m_dwAdvise;
+
+ // advise time is for this presentation time
+ CRefTime m_tCurrentAdvise;
+
+ // the reference clock we are using (addrefed)
+ IReferenceClock* m_pClock;
+
+ // true when running
+ BOOL m_bRunning;
+
+ // contains stream time offset when m_bRunning is true
+ CRefTime m_StreamTimeOffset;
+};
+
+#endif // __CTLUTIL__
diff --git a/dshow_base/ddmm.cpp b/dshow_base/ddmm.cpp
new file mode 100644
index 0000000..bfa700c
--- /dev/null
+++ b/dshow_base/ddmm.cpp
@@ -0,0 +1,129 @@
+//------------------------------------------------------------------------------
+// File: DDMM.cpp
+//
+// Desc: DirectShow base classes - implements routines for using DirectDraw
+// on a multimonitor system.
+//
+// Copyright (c) 1995-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#include
+#include "ddmm.h"
+
+/*
+ * FindDeviceCallback
+ */
+typedef struct {
+ LPSTR szDevice;
+ GUID* lpGUID;
+ GUID GUID;
+ BOOL fFound;
+} FindDeviceData;
+
+BOOL CALLBACK FindDeviceCallback(__in_opt GUID* lpGUID, __in LPSTR szName, __in LPSTR szDevice, __in LPVOID lParam)
+{
+ FindDeviceData *p = (FindDeviceData*)lParam;
+
+ if (lstrcmpiA(p->szDevice, szDevice) == 0) {
+ if (lpGUID) {
+ p->GUID = *lpGUID;
+ p->lpGUID = &p->GUID;
+ } else {
+ p->lpGUID = NULL;
+ }
+ p->fFound = TRUE;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+BOOL CALLBACK FindDeviceCallbackEx(__in_opt GUID* lpGUID, __in LPSTR szName, __in LPSTR szDevice, __in LPVOID lParam, HMONITOR hMonitor)
+{
+ FindDeviceData *p = (FindDeviceData*)lParam;
+
+ if (lstrcmpiA(p->szDevice, szDevice) == 0) {
+ if (lpGUID) {
+ p->GUID = *lpGUID;
+ p->lpGUID = &p->GUID;
+ } else {
+ p->lpGUID = NULL;
+ }
+ p->fFound = TRUE;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+ * DirectDrawCreateFromDevice
+ *
+ * create a DirectDraw object for a particular device
+ */
+IDirectDraw * DirectDrawCreateFromDevice(__in_opt LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, PDRAWENUM DirectDrawEnumerateP)
+{
+ IDirectDraw* pdd = NULL;
+ FindDeviceData find;
+
+ if (szDevice == NULL) {
+ DirectDrawCreateP(NULL, &pdd, NULL);
+ return pdd;
+ }
+
+ find.szDevice = szDevice;
+ find.fFound = FALSE;
+ DirectDrawEnumerateP(FindDeviceCallback, (LPVOID)&find);
+
+ if (find.fFound)
+ {
+ //
+ // In 4bpp mode the following DDraw call causes a message box to be popped
+ // up by DDraw (!?!). It's DDraw's fault, but we don't like it. So we
+ // make sure it doesn't happen.
+ //
+ UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
+ DirectDrawCreateP(find.lpGUID, &pdd, NULL);
+ SetErrorMode(ErrorMode);
+ }
+
+ return pdd;
+}
+
+
+/*
+ * DirectDrawCreateFromDeviceEx
+ *
+ * create a DirectDraw object for a particular device
+ */
+IDirectDraw * DirectDrawCreateFromDeviceEx(__in_opt LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, LPDIRECTDRAWENUMERATEEXA DirectDrawEnumerateExP)
+{
+ IDirectDraw* pdd = NULL;
+ FindDeviceData find;
+
+ if (szDevice == NULL) {
+ DirectDrawCreateP(NULL, &pdd, NULL);
+ return pdd;
+ }
+
+ find.szDevice = szDevice;
+ find.fFound = FALSE;
+ DirectDrawEnumerateExP(FindDeviceCallbackEx, (LPVOID)&find,
+ DDENUM_ATTACHEDSECONDARYDEVICES);
+
+ if (find.fFound)
+ {
+ //
+ // In 4bpp mode the following DDraw call causes a message box to be popped
+ // up by DDraw (!?!). It's DDraw's fault, but we don't like it. So we
+ // make sure it doesn't happen.
+ //
+ UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
+ DirectDrawCreateP(find.lpGUID, &pdd, NULL);
+ SetErrorMode(ErrorMode);
+ }
+
+ return pdd;
+}
diff --git a/dshow_base/ddmm.h b/dshow_base/ddmm.h
index c790754..7b311bc 100644
--- a/dshow_base/ddmm.h
+++ b/dshow_base/ddmm.h
@@ -1,28 +1,28 @@
-//------------------------------------------------------------------------------
-// File: DDMM.h
-//
-// Desc: DirectShow base classes - efines routines for using DirectDraw
-// on a multimonitor system.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifdef __cplusplus
-extern "C" { /* Assume C declarations for C++ */
-#endif /* __cplusplus */
-
-// DDRAW.H might not include these
-#ifndef DDENUM_ATTACHEDSECONDARYDEVICES
-#define DDENUM_ATTACHEDSECONDARYDEVICES 0x00000001L
-#endif
-
-typedef HRESULT (*PDRAWCREATE)(IID *,LPDIRECTDRAW *,LPUNKNOWN);
-typedef HRESULT (*PDRAWENUM)(LPDDENUMCALLBACKA, LPVOID);
-
-IDirectDraw * DirectDrawCreateFromDevice(LPSTR, PDRAWCREATE, PDRAWENUM);
-IDirectDraw * DirectDrawCreateFromDeviceEx(LPSTR, PDRAWCREATE, LPDIRECTDRAWENUMERATEEXA);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+//------------------------------------------------------------------------------
+// File: DDMM.h
+//
+// Desc: DirectShow base classes - efines routines for using DirectDraw
+// on a multimonitor system.
+//
+// Copyright (c) 1995-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifdef __cplusplus
+extern "C" { /* Assume C declarations for C++ */
+#endif /* __cplusplus */
+
+// DDRAW.H might not include these
+#ifndef DDENUM_ATTACHEDSECONDARYDEVICES
+#define DDENUM_ATTACHEDSECONDARYDEVICES 0x00000001L
+#endif
+
+typedef HRESULT (*PDRAWCREATE)(IID *,LPDIRECTDRAW *,LPUNKNOWN);
+typedef HRESULT (*PDRAWENUM)(LPDDENUMCALLBACKA, LPVOID);
+
+IDirectDraw * DirectDrawCreateFromDevice(__in_opt LPSTR, PDRAWCREATE, PDRAWENUM);
+IDirectDraw * DirectDrawCreateFromDeviceEx(__in_opt LPSTR, PDRAWCREATE, LPDIRECTDRAWENUMERATEEXA);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
diff --git a/dshow_base/dllentry.cpp b/dshow_base/dllentry.cpp
new file mode 100644
index 0000000..130aad6
--- /dev/null
+++ b/dshow_base/dllentry.cpp
@@ -0,0 +1,367 @@
+//------------------------------------------------------------------------------
+// File: DlleEntry.cpp
+//
+// Desc: DirectShow base classes - implements classes used to support dll
+// entry points for COM objects.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#include
+
+#ifdef DEBUG
+#ifdef UNICODE
+#ifndef _UNICODE
+#define _UNICODE
+#endif // _UNICODE
+#endif // UNICODE
+
+#include
+#endif // DEBUG
+#include
+
+extern CFactoryTemplate g_Templates[];
+extern int g_cTemplates;
+
+HINSTANCE g_hInst;
+DWORD g_amPlatform; // VER_PLATFORM_WIN32_WINDOWS etc... (from GetVersionEx)
+OSVERSIONINFO g_osInfo;
+
+//
+// an instance of this is created by the DLLGetClassObject entrypoint
+// it uses the CFactoryTemplate object it is given to support the
+// IClassFactory interface
+
+class CClassFactory : public IClassFactory, public CBaseObject
+{
+
+private:
+ const CFactoryTemplate *const m_pTemplate;
+
+ ULONG m_cRef;
+
+ static int m_cLocked;
+public:
+ CClassFactory(const CFactoryTemplate *);
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, __deref_out void ** ppv);
+ STDMETHODIMP_(ULONG)AddRef();
+ STDMETHODIMP_(ULONG)Release();
+
+ // IClassFactory
+ STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, __deref_out void **pv);
+ STDMETHODIMP LockServer(BOOL fLock);
+
+ // allow DLLGetClassObject to know about global server lock status
+ static BOOL IsLocked() {
+ return (m_cLocked > 0);
+ };
+};
+
+// process-wide dll locked state
+int CClassFactory::m_cLocked = 0;
+
+CClassFactory::CClassFactory(const CFactoryTemplate *pTemplate)
+: CBaseObject(NAME("Class Factory"))
+, m_cRef(0)
+, m_pTemplate(pTemplate)
+{
+}
+
+
+STDMETHODIMP
+CClassFactory::QueryInterface(REFIID riid,__deref_out void **ppv)
+{
+ CheckPointer(ppv,E_POINTER)
+ ValidateReadWritePtr(ppv,sizeof(PVOID));
+ *ppv = NULL;
+
+ // any interface on this object is the object pointer.
+ if ((riid == IID_IUnknown) || (riid == IID_IClassFactory)) {
+ *ppv = (LPVOID) this;
+ // AddRef returned interface pointer
+ ((LPUNKNOWN) *ppv)->AddRef();
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+
+STDMETHODIMP_(ULONG)
+CClassFactory::AddRef()
+{
+ return ++m_cRef;
+}
+
+STDMETHODIMP_(ULONG)
+CClassFactory::Release()
+{
+ LONG lRef = InterlockedDecrement((volatile LONG *)&m_cRef);
+ if (lRef == 0) {
+ delete this;
+ return 0;
+ } else {
+ return lRef;
+ }
+}
+
+STDMETHODIMP
+CClassFactory::CreateInstance(
+ LPUNKNOWN pUnkOuter,
+ REFIID riid,
+ __deref_out void **pv)
+{
+ CheckPointer(pv,E_POINTER)
+ ValidateReadWritePtr(pv,sizeof(void *));
+ *pv = NULL;
+
+ /* Enforce the normal OLE rules regarding interfaces and delegation */
+
+ if (pUnkOuter != NULL) {
+ if (IsEqualIID(riid,IID_IUnknown) == FALSE) {
+ *pv = NULL;
+ return ResultFromScode(E_NOINTERFACE);
+ }
+ }
+
+ /* Create the new object through the derived class's create function */
+
+ HRESULT hr = NOERROR;
+ CUnknown *pObj = m_pTemplate->CreateInstance(pUnkOuter, &hr);
+
+ if (pObj == NULL) {
+ *pv = NULL;
+ if (SUCCEEDED(hr)) {
+ hr = E_OUTOFMEMORY;
+ }
+ return hr;
+ }
+
+ /* Delete the object if we got a construction error */
+
+ if (FAILED(hr)) {
+ delete pObj;
+ *pv = NULL;
+ return hr;
+ }
+
+ /* Get a reference counted interface on the object */
+
+ /* We wrap the non-delegating QI with NDAddRef & NDRelease. */
+ /* This protects any outer object from being prematurely */
+ /* released by an inner object that may have to be created */
+ /* in order to supply the requested interface. */
+ pObj->NonDelegatingAddRef();
+ hr = pObj->NonDelegatingQueryInterface(riid, pv);
+ pObj->NonDelegatingRelease();
+ /* Note that if NonDelegatingQueryInterface fails, it will */
+ /* not increment the ref count, so the NonDelegatingRelease */
+ /* will drop the ref back to zero and the object will "self-*/
+ /* destruct". Hence we don't need additional tidy-up code */
+ /* to cope with NonDelegatingQueryInterface failing. */
+
+ if (SUCCEEDED(hr)) {
+ ASSERT(*pv);
+ }
+
+ return hr;
+}
+
+STDMETHODIMP
+CClassFactory::LockServer(BOOL fLock)
+{
+ if (fLock) {
+ m_cLocked++;
+ } else {
+ m_cLocked--;
+ }
+ return NOERROR;
+}
+
+
+// --- COM entrypoints -----------------------------------------
+
+//called by COM to get the class factory object for a given class
+__control_entrypoint(DllExport) STDAPI
+DllGetClassObject(
+ __in REFCLSID rClsID,
+ __in REFIID riid,
+ __deref_out void **pv)
+{
+ *pv = NULL;
+ if (!(riid == IID_IUnknown) && !(riid == IID_IClassFactory)) {
+ return E_NOINTERFACE;
+ }
+
+ // traverse the array of templates looking for one with this
+ // class id
+ for (int i = 0; i < g_cTemplates; i++) {
+ const CFactoryTemplate * pT = &g_Templates[i];
+ if (pT->IsClassID(rClsID)) {
+
+ // found a template - make a class factory based on this
+ // template
+
+ *pv = (LPVOID) (LPUNKNOWN) new CClassFactory(pT);
+ if (*pv == NULL) {
+ return E_OUTOFMEMORY;
+ }
+ ((LPUNKNOWN)*pv)->AddRef();
+ return NOERROR;
+ }
+ }
+ return CLASS_E_CLASSNOTAVAILABLE;
+}
+
+//
+// Call any initialization routines
+//
+void
+DllInitClasses(BOOL bLoading)
+{
+ int i;
+
+ // traverse the array of templates calling the init routine
+ // if they have one
+ for (i = 0; i < g_cTemplates; i++) {
+ const CFactoryTemplate * pT = &g_Templates[i];
+ if (pT->m_lpfnInit != NULL) {
+ (*pT->m_lpfnInit)(bLoading, pT->m_ClsID);
+ }
+ }
+
+}
+
+// called by COM to determine if this dll can be unloaded
+// return ok unless there are outstanding objects or a lock requested
+// by IClassFactory::LockServer
+//
+// CClassFactory has a static function that can tell us about the locks,
+// and CCOMObject has a static function that can tell us about the active
+// object count
+STDAPI
+DllCanUnloadNow()
+{
+ DbgLog((LOG_MEMORY,2,TEXT("DLLCanUnloadNow called - IsLocked = %d, Active objects = %d"),
+ CClassFactory::IsLocked(),
+ CBaseObject::ObjectsActive()));
+
+ if (CClassFactory::IsLocked() || CBaseObject::ObjectsActive()) {
+ return S_FALSE;
+ } else {
+ return S_OK;
+ }
+}
+
+
+// --- standard WIN32 entrypoints --------------------------------------
+
+
+extern "C" void __cdecl __security_init_cookie(void);
+extern "C" BOOL WINAPI _DllEntryPoint(HINSTANCE, ULONG, __inout_opt LPVOID);
+#pragma comment(linker, "/merge:.CRT=.rdata")
+
+extern "C"
+DECLSPEC_NOINLINE
+BOOL
+WINAPI
+DllEntryPoint(
+ HINSTANCE hInstance,
+ ULONG ulReason,
+ __inout_opt LPVOID pv
+ )
+{
+ if ( ulReason == DLL_PROCESS_ATTACH ) {
+ // Must happen before any other code is executed. Thankfully - it's re-entrant
+ __security_init_cookie();
+ }
+ return _DllEntryPoint(hInstance, ulReason, pv);
+}
+
+
+DECLSPEC_NOINLINE
+BOOL
+WINAPI
+_DllEntryPoint(
+ HINSTANCE hInstance,
+ ULONG ulReason,
+ __inout_opt LPVOID pv
+ )
+{
+#ifdef DEBUG
+ extern bool g_fDbgInDllEntryPoint;
+ g_fDbgInDllEntryPoint = true;
+#endif
+
+ switch (ulReason)
+ {
+
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hInstance);
+ DbgInitialise(hInstance);
+
+ {
+ // The platform identifier is used to work out whether
+ // full unicode support is available or not. Hence the
+ // default will be the lowest common denominator - i.e. N/A
+ g_amPlatform = VER_PLATFORM_WIN32_WINDOWS; // win95 assumed in case GetVersionEx fails
+
+ g_osInfo.dwOSVersionInfoSize = sizeof(g_osInfo);
+ if (GetVersionEx(&g_osInfo)) {
+ g_amPlatform = g_osInfo.dwPlatformId;
+ } else {
+ DbgLog((LOG_ERROR, 1, TEXT("Failed to get the OS platform, assuming Win95")));
+ }
+ }
+
+ g_hInst = hInstance;
+ DllInitClasses(TRUE);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ DllInitClasses(FALSE);
+
+#ifdef DEBUG
+ if (CBaseObject::ObjectsActive()) {
+ DbgSetModuleLevel(LOG_MEMORY, 2);
+ TCHAR szInfo[512];
+ extern TCHAR m_ModuleName[]; // Cut down module name
+
+ TCHAR FullName[_MAX_PATH]; // Load the full path and module name
+ TCHAR *pName; // Searches from the end for a backslash
+
+ GetModuleFileName(NULL,FullName,_MAX_PATH);
+ pName = _tcsrchr(FullName,'\\');
+ if (pName == NULL) {
+ pName = FullName;
+ } else {
+ pName++;
+ }
+
+ (void)StringCchPrintf(szInfo, NUMELMS(szInfo), TEXT("Executable: %s Pid %x Tid %x. "),
+ pName, GetCurrentProcessId(), GetCurrentThreadId());
+
+ (void)StringCchPrintf(szInfo+lstrlen(szInfo), NUMELMS(szInfo) - lstrlen(szInfo), TEXT("Module %s, %d objects left active!"),
+ m_ModuleName, CBaseObject::ObjectsActive());
+ DbgAssert(szInfo, TEXT(__FILE__),__LINE__);
+
+ // If running remotely wait for the Assert to be acknowledged
+ // before dumping out the object register
+ DbgDumpObjectRegister();
+ }
+ DbgTerminate();
+#endif
+ break;
+ }
+
+#ifdef DEBUG
+ g_fDbgInDllEntryPoint = false;
+#endif
+ return TRUE;
+}
+
+
diff --git a/dshow_base/dllsetup.cpp b/dshow_base/dllsetup.cpp
new file mode 100644
index 0000000..ede9c3f
--- /dev/null
+++ b/dshow_base/dllsetup.cpp
@@ -0,0 +1,693 @@
+//------------------------------------------------------------------------------
+// File: DllSetup.cpp
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#include
+
+//---------------------------------------------------------------------------
+// defines
+
+#define MAX_KEY_LEN 260
+
+
+//---------------------------------------------------------------------------
+// externally defined functions/variable
+
+extern int g_cTemplates;
+extern CFactoryTemplate g_Templates[];
+
+//---------------------------------------------------------------------------
+//
+// EliminateSubKey
+//
+// Try to enumerate all keys under this one.
+// if we find anything, delete it completely.
+// Otherwise just delete it.
+//
+// note - this was pinched/duplicated from
+// Filgraph\Mapper.cpp - so should it be in
+// a lib somewhere?
+//
+//---------------------------------------------------------------------------
+
+STDAPI
+EliminateSubKey( HKEY hkey, LPCTSTR strSubKey )
+{
+ HKEY hk;
+ if (0 == lstrlen(strSubKey) ) {
+ // defensive approach
+ return E_FAIL;
+ }
+
+ LONG lreturn = RegOpenKeyEx( hkey
+ , strSubKey
+ , 0
+ , MAXIMUM_ALLOWED
+ , &hk );
+
+ ASSERT( lreturn == ERROR_SUCCESS
+ || lreturn == ERROR_FILE_NOT_FOUND
+ || lreturn == ERROR_INVALID_HANDLE );
+
+ if( ERROR_SUCCESS == lreturn )
+ {
+ // Keep on enumerating the first (zero-th)
+ // key and deleting that
+
+ for( ; ; )
+ {
+ TCHAR Buffer[MAX_KEY_LEN];
+ DWORD dw = MAX_KEY_LEN;
+ FILETIME ft;
+
+ lreturn = RegEnumKeyEx( hk
+ , 0
+ , Buffer
+ , &dw
+ , NULL
+ , NULL
+ , NULL
+ , &ft);
+
+ ASSERT( lreturn == ERROR_SUCCESS
+ || lreturn == ERROR_NO_MORE_ITEMS );
+
+ if( ERROR_SUCCESS == lreturn )
+ {
+ EliminateSubKey(hk, Buffer);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ RegCloseKey(hk);
+ RegDeleteKey(hkey, strSubKey);
+ }
+
+ return NOERROR;
+}
+
+
+//---------------------------------------------------------------------------
+//
+// AMovieSetupRegisterServer()
+//
+// registers specfied file "szFileName" as server for
+// CLSID "clsServer". A description is also required.
+// The ThreadingModel and ServerType are optional, as
+// they default to InprocServer32 (i.e. dll) and Both.
+//
+//---------------------------------------------------------------------------
+
+STDAPI
+AMovieSetupRegisterServer( CLSID clsServer
+ , LPCWSTR szDescription
+ , LPCWSTR szFileName
+ , LPCWSTR szThreadingModel = L"Both"
+ , LPCWSTR szServerType = L"InprocServer32" )
+{
+ // temp buffer
+ //
+ TCHAR achTemp[MAX_PATH];
+
+ // convert CLSID uuid to string and write
+ // out subkey as string - CLSID\{}
+ //
+ OLECHAR szCLSID[CHARS_IN_GUID];
+ HRESULT hr = StringFromGUID2( clsServer
+ , szCLSID
+ , CHARS_IN_GUID );
+ ASSERT( SUCCEEDED(hr) );
+
+ // create key
+ //
+ HKEY hkey;
+ (void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("CLSID\\%ls"), szCLSID );
+ LONG lreturn = RegCreateKey( HKEY_CLASSES_ROOT
+ , (LPCTSTR)achTemp
+ , &hkey );
+ if( ERROR_SUCCESS != lreturn )
+ {
+ return AmHresultFromWin32(lreturn);
+ }
+
+ // set description string
+ //
+
+ (void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szDescription );
+ lreturn = RegSetValue( hkey
+ , (LPCTSTR)NULL
+ , REG_SZ
+ , achTemp
+ , sizeof(achTemp) );
+ if( ERROR_SUCCESS != lreturn )
+ {
+ RegCloseKey( hkey );
+ return AmHresultFromWin32(lreturn);
+ }
+
+ // create CLSID\\{"CLSID"}\\"ServerType" key,
+ // using key to CLSID\\{"CLSID"} passed back by
+ // last call to RegCreateKey().
+ //
+ HKEY hsubkey;
+
+ (void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szServerType );
+ lreturn = RegCreateKey( hkey
+ , achTemp
+ , &hsubkey );
+ if( ERROR_SUCCESS != lreturn )
+ {
+ RegCloseKey( hkey );
+ return AmHresultFromWin32(lreturn);
+ }
+
+ // set Server string
+ //
+ (void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szFileName );
+ lreturn = RegSetValue( hsubkey
+ , (LPCTSTR)NULL
+ , REG_SZ
+ , (LPCTSTR)achTemp
+ , sizeof(TCHAR) * (lstrlen(achTemp)+1) );
+ if( ERROR_SUCCESS != lreturn )
+ {
+ RegCloseKey( hkey );
+ RegCloseKey( hsubkey );
+ return AmHresultFromWin32(lreturn);
+ }
+
+ (void)StringCchPrintf( achTemp, NUMELMS(achTemp), TEXT("%ls"), szThreadingModel );
+ lreturn = RegSetValueEx( hsubkey
+ , TEXT("ThreadingModel")
+ , 0L
+ , REG_SZ
+ , (CONST BYTE *)achTemp
+ , sizeof(TCHAR) * (lstrlen(achTemp)+1) );
+
+ // close hkeys
+ //
+ RegCloseKey( hkey );
+ RegCloseKey( hsubkey );
+
+ // and return
+ //
+ return HRESULT_FROM_WIN32(lreturn);
+
+}
+
+
+//---------------------------------------------------------------------------
+//
+// AMovieSetupUnregisterServer()
+//
+// default ActiveMovie dll setup function
+// - to use must be called from an exported
+// function named DllRegisterServer()
+//
+//---------------------------------------------------------------------------
+
+STDAPI
+AMovieSetupUnregisterServer( CLSID clsServer )
+{
+ // convert CLSID uuid to string and write
+ // out subkey CLSID\{}
+ //
+ OLECHAR szCLSID[CHARS_IN_GUID];
+ HRESULT hr = StringFromGUID2( clsServer
+ , szCLSID
+ , CHARS_IN_GUID );
+ ASSERT( SUCCEEDED(hr) );
+
+ TCHAR achBuffer[MAX_KEY_LEN];
+ (void)StringCchPrintf( achBuffer, NUMELMS(achBuffer), TEXT("CLSID\\%ls"), szCLSID );
+
+ // delete subkey
+ //
+
+ hr = EliminateSubKey( HKEY_CLASSES_ROOT, achBuffer );
+ ASSERT( SUCCEEDED(hr) );
+
+ // return
+ //
+ return NOERROR;
+}
+
+
+//---------------------------------------------------------------------------
+//
+// AMovieSetupRegisterFilter through IFilterMapper2
+//
+//---------------------------------------------------------------------------
+
+STDAPI
+AMovieSetupRegisterFilter2( const AMOVIESETUP_FILTER * const psetupdata
+ , IFilterMapper2 * pIFM2
+ , BOOL bRegister )
+{
+ DbgLog((LOG_TRACE, 3, TEXT("= AMovieSetupRegisterFilter")));
+
+ // check we've got data
+ //
+ if( NULL == psetupdata ) return S_FALSE;
+
+
+ // unregister filter
+ // (as pins are subkeys of filter's CLSID key
+ // they do not need to be removed separately).
+ //
+ DbgLog((LOG_TRACE, 3, TEXT("= = unregister filter")));
+ HRESULT hr = pIFM2->UnregisterFilter(
+ 0, // default category
+ 0, // default instance name
+ *psetupdata->clsID );
+
+
+ if( bRegister )
+ {
+ REGFILTER2 rf2;
+ rf2.dwVersion = 1;
+ rf2.dwMerit = psetupdata->dwMerit;
+ rf2.cPins = psetupdata->nPins;
+ rf2.rgPins = psetupdata->lpPin;
+
+ // register filter
+ //
+ DbgLog((LOG_TRACE, 3, TEXT("= = register filter")));
+ hr = pIFM2->RegisterFilter(*psetupdata->clsID
+ , psetupdata->strName
+ , 0 // moniker
+ , 0 // category
+ , NULL // instance
+ , &rf2);
+ }
+
+ // handle one acceptable "error" - that
+ // of filter not being registered!
+ // (couldn't find a suitable #define'd
+ // name for the error!)
+ //
+ if( 0x80070002 == hr)
+ return NOERROR;
+ else
+ return hr;
+}
+
+
+//---------------------------------------------------------------------------
+//
+// RegisterAllServers()
+//
+//---------------------------------------------------------------------------
+
+STDAPI
+RegisterAllServers( LPCWSTR szFileName, BOOL bRegister )
+{
+ HRESULT hr = NOERROR;
+
+ for( int i = 0; i < g_cTemplates; i++ )
+ {
+ // get i'th template
+ //
+ const CFactoryTemplate *pT = &g_Templates[i];
+
+ DbgLog((LOG_TRACE, 2, TEXT("- - register %ls"),
+ (LPCWSTR)pT->m_Name ));
+
+ // register CLSID and InprocServer32
+ //
+ if( bRegister )
+ {
+ hr = AMovieSetupRegisterServer( *(pT->m_ClsID)
+ , (LPCWSTR)pT->m_Name
+ , szFileName );
+ }
+ else
+ {
+ hr = AMovieSetupUnregisterServer( *(pT->m_ClsID) );
+ }
+
+ // check final error for this pass
+ // and break loop if we failed
+ //
+ if( FAILED(hr) )
+ break;
+ }
+
+ return hr;
+}
+
+
+//---------------------------------------------------------------------------
+//
+// AMovieDllRegisterServer2()
+//
+// default ActiveMovie dll setup function
+// - to use must be called from an exported
+// function named DllRegisterServer()
+//
+// this function is table driven using the
+// static members of the CFactoryTemplate
+// class defined in the dll.
+//
+// it registers the Dll as the InprocServer32
+// and then calls the IAMovieSetup.Register
+// method.
+//
+//---------------------------------------------------------------------------
+
+STDAPI
+AMovieDllRegisterServer2( BOOL bRegister )
+{
+ HRESULT hr = NOERROR;
+
+ DbgLog((LOG_TRACE, 2, TEXT("AMovieDllRegisterServer2()")));
+
+ // get file name (where g_hInst is the
+ // instance handle of the filter dll)
+ //
+ WCHAR achFileName[MAX_PATH];
+
+ // WIN95 doesn't support GetModuleFileNameW
+ //
+ {
+ char achTemp[MAX_PATH];
+
+ DbgLog((LOG_TRACE, 2, TEXT("- get module file name")));
+
+ // g_hInst handle is set in our dll entry point. Make sure
+ // DllEntryPoint in dllentry.cpp is called
+ ASSERT(g_hInst != 0);
+
+ if( 0 == GetModuleFileNameA( g_hInst
+ , achTemp
+ , sizeof(achTemp) ) )
+ {
+ // we've failed!
+ DWORD dwerr = GetLastError();
+ return AmHresultFromWin32(dwerr);
+ }
+
+ MultiByteToWideChar( CP_ACP
+ , 0L
+ , achTemp
+ , lstrlenA(achTemp) + 1
+ , achFileName
+ , NUMELMS(achFileName) );
+ }
+
+ //
+ // first registering, register all OLE servers
+ //
+ if( bRegister )
+ {
+ DbgLog((LOG_TRACE, 2, TEXT("- register OLE Servers")));
+ hr = RegisterAllServers( achFileName, TRUE );
+ }
+
+ //
+ // next, register/unregister all filters
+ //
+
+ if( SUCCEEDED(hr) )
+ {
+ // init is ref counted so call just in case
+ // we're being called cold.
+ //
+ DbgLog((LOG_TRACE, 2, TEXT("- CoInitialize")));
+ hr = CoInitialize( (LPVOID)NULL );
+ ASSERT( SUCCEEDED(hr) );
+
+ // get hold of IFilterMapper2
+ //
+ DbgLog((LOG_TRACE, 2, TEXT("- obtain IFilterMapper2")));
+ IFilterMapper2 *pIFM2 = 0;
+ IFilterMapper *pIFM = 0;
+ hr = CoCreateInstance( CLSID_FilterMapper2
+ , NULL
+ , CLSCTX_INPROC_SERVER
+ , IID_IFilterMapper2
+ , (void **)&pIFM2 );
+ if(FAILED(hr))
+ {
+ DbgLog((LOG_TRACE, 2, TEXT("- trying IFilterMapper instead")));
+
+ hr = CoCreateInstance(
+ CLSID_FilterMapper,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IFilterMapper,
+ (void **)&pIFM);
+ }
+ if( SUCCEEDED(hr) )
+ {
+ // scan through array of CFactoryTemplates
+ // registering servers and filters.
+ //
+ DbgLog((LOG_TRACE, 2, TEXT("- register Filters")));
+ for( int i = 0; i < g_cTemplates; i++ )
+ {
+ // get i'th template
+ //
+ const CFactoryTemplate *pT = &g_Templates[i];
+
+ if( NULL != pT->m_pAMovieSetup_Filter )
+ {
+ DbgLog((LOG_TRACE, 2, TEXT("- - register %ls"), (LPCWSTR)pT->m_Name ));
+
+ if(pIFM2)
+ {
+ hr = AMovieSetupRegisterFilter2( pT->m_pAMovieSetup_Filter, pIFM2, bRegister );
+ }
+ else
+ {
+ hr = AMovieSetupRegisterFilter( pT->m_pAMovieSetup_Filter, pIFM, bRegister );
+ }
+ }
+
+ // check final error for this pass
+ // and break loop if we failed
+ //
+ if( FAILED(hr) )
+ break;
+ }
+
+ // release interface
+ //
+ if(pIFM2)
+ pIFM2->Release();
+ else
+ pIFM->Release();
+
+ }
+
+ // and clear up
+ //
+ CoFreeUnusedLibraries();
+ CoUninitialize();
+ }
+
+ //
+ // if unregistering, unregister all OLE servers
+ //
+ if( SUCCEEDED(hr) && !bRegister )
+ {
+ DbgLog((LOG_TRACE, 2, TEXT("- register OLE Servers")));
+ hr = RegisterAllServers( achFileName, FALSE );
+ }
+
+ DbgLog((LOG_TRACE, 2, TEXT("- return %0x"), hr));
+ return hr;
+}
+
+
+//---------------------------------------------------------------------------
+//
+// AMovieDllRegisterServer()
+//
+// default ActiveMovie dll setup function
+// - to use must be called from an exported
+// function named DllRegisterServer()
+//
+// this function is table driven using the
+// static members of the CFactoryTemplate
+// class defined in the dll.
+//
+// it registers the Dll as the InprocServer32
+// and then calls the IAMovieSetup.Register
+// method.
+//
+//---------------------------------------------------------------------------
+
+
+STDAPI
+AMovieDllRegisterServer( void )
+{
+ HRESULT hr = NOERROR;
+
+ // get file name (where g_hInst is the
+ // instance handle of the filter dll)
+ //
+ WCHAR achFileName[MAX_PATH];
+
+ {
+ // WIN95 doesn't support GetModuleFileNameW
+ //
+ char achTemp[MAX_PATH];
+
+ if( 0 == GetModuleFileNameA( g_hInst
+ , achTemp
+ , sizeof(achTemp) ) )
+ {
+ // we've failed!
+ DWORD dwerr = GetLastError();
+ return AmHresultFromWin32(dwerr);
+ }
+
+ MultiByteToWideChar( CP_ACP
+ , 0L
+ , achTemp
+ , lstrlenA(achTemp) + 1
+ , achFileName
+ , NUMELMS(achFileName) );
+ }
+
+ // scan through array of CFactoryTemplates
+ // registering servers and filters.
+ //
+ for( int i = 0; i < g_cTemplates; i++ )
+ {
+ // get i'th template
+ //
+ const CFactoryTemplate *pT = &g_Templates[i];
+
+ // register CLSID and InprocServer32
+ //
+ hr = AMovieSetupRegisterServer( *(pT->m_ClsID)
+ , (LPCWSTR)pT->m_Name
+ , achFileName );
+
+ // instantiate all servers and get hold of
+ // IAMovieSetup, if implemented, and call
+ // IAMovieSetup.Register() method
+ //
+ if( SUCCEEDED(hr) && (NULL != pT->m_lpfnNew) )
+ {
+ // instantiate object
+ //
+ PAMOVIESETUP psetup;
+ hr = CoCreateInstance( *(pT->m_ClsID)
+ , 0
+ , CLSCTX_INPROC_SERVER
+ , IID_IAMovieSetup
+ , reinterpret_cast(&psetup) );
+ if( SUCCEEDED(hr) )
+ {
+ hr = psetup->Unregister();
+ if( SUCCEEDED(hr) )
+ hr = psetup->Register();
+ psetup->Release();
+ }
+ else
+ {
+ if( (E_NOINTERFACE == hr )
+ || (VFW_E_NEED_OWNER == hr ) )
+ hr = NOERROR;
+ }
+ }
+
+ // check final error for this pass
+ // and break loop if we failed
+ //
+ if( FAILED(hr) )
+ break;
+
+ } // end-for
+
+ return hr;
+}
+
+
+//---------------------------------------------------------------------------
+//
+// AMovieDllUnregisterServer()
+//
+// default ActiveMovie dll uninstall function
+// - to use must be called from an exported
+// function named DllRegisterServer()
+//
+// this function is table driven using the
+// static members of the CFactoryTemplate
+// class defined in the dll.
+//
+// it calls the IAMovieSetup.Unregister
+// method and then unregisters the Dll
+// as the InprocServer32
+//
+//---------------------------------------------------------------------------
+
+STDAPI
+AMovieDllUnregisterServer()
+{
+ // initialize return code
+ //
+ HRESULT hr = NOERROR;
+
+ // scan through CFactory template and unregister
+ // all OLE servers and filters.
+ //
+ for( int i = g_cTemplates; i--; )
+ {
+ // get i'th template
+ //
+ const CFactoryTemplate *pT = &g_Templates[i];
+
+ // check method exists
+ //
+ if( NULL != pT->m_lpfnNew )
+ {
+ // instantiate object
+ //
+ PAMOVIESETUP psetup;
+ hr = CoCreateInstance( *(pT->m_ClsID)
+ , 0
+ , CLSCTX_INPROC_SERVER
+ , IID_IAMovieSetup
+ , reinterpret_cast(&psetup) );
+ if( SUCCEEDED(hr) )
+ {
+ hr = psetup->Unregister();
+ psetup->Release();
+ }
+ else
+ {
+ if( (E_NOINTERFACE == hr )
+ || (VFW_E_NEED_OWNER == hr ) )
+ hr = NOERROR;
+ }
+ }
+
+ // unregister CLSID and InprocServer32
+ //
+ if( SUCCEEDED(hr) )
+ {
+ hr = AMovieSetupUnregisterServer( *(pT->m_ClsID) );
+ }
+
+ // check final error for this pass
+ // and break loop if we failed
+ //
+ if( FAILED(hr) )
+ break;
+ }
+
+ return hr;
+}
diff --git a/dshow_base/dllsetup.h b/dshow_base/dllsetup.h
index 4c5ac6d..e363b8b 100644
--- a/dshow_base/dllsetup.h
+++ b/dshow_base/dllsetup.h
@@ -1,46 +1,46 @@
-//------------------------------------------------------------------------------
-// File: DllSetup.h
-//
-// Desc: DirectShow base classes.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-// To be self registering, OLE servers must
-// export functions named DllRegisterServer
-// and DllUnregisterServer. To allow use of
-// custom and default implementations the
-// defaults are named AMovieDllRegisterServer
-// and AMovieDllUnregisterServer.
-//
-// To the use the default implementation you
-// must provide stub functions.
-//
-// i.e. STDAPI DllRegisterServer()
-// {
-// return AMovieDllRegisterServer();
-// }
-//
-// STDAPI DllUnregisterServer()
-// {
-// return AMovieDllUnregisterServer();
-// }
-//
-//
-// AMovieDllRegisterServer calls IAMovieSetup.Register(), and
-// AMovieDllUnregisterServer calls IAMovieSetup.Unregister().
-
-STDAPI AMovieDllRegisterServer2( BOOL );
-STDAPI AMovieDllRegisterServer();
-STDAPI AMovieDllUnregisterServer();
-
-// helper functions
-STDAPI EliminateSubKey( HKEY, LPTSTR );
-
-
-STDAPI
-AMovieSetupRegisterFilter2( const AMOVIESETUP_FILTER * const psetupdata
- , IFilterMapper2 * pIFM2
- , BOOL bRegister );
-
+//------------------------------------------------------------------------------
+// File: DllSetup.h
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+// To be self registering, OLE servers must
+// export functions named DllRegisterServer
+// and DllUnregisterServer. To allow use of
+// custom and default implementations the
+// defaults are named AMovieDllRegisterServer
+// and AMovieDllUnregisterServer.
+//
+// To the use the default implementation you
+// must provide stub functions.
+//
+// i.e. STDAPI DllRegisterServer()
+// {
+// return AMovieDllRegisterServer();
+// }
+//
+// STDAPI DllUnregisterServer()
+// {
+// return AMovieDllUnregisterServer();
+// }
+//
+//
+// AMovieDllRegisterServer calls IAMovieSetup.Register(), and
+// AMovieDllUnregisterServer calls IAMovieSetup.Unregister().
+
+STDAPI AMovieDllRegisterServer2( BOOL );
+STDAPI AMovieDllRegisterServer();
+STDAPI AMovieDllUnregisterServer();
+
+// helper functions
+STDAPI EliminateSubKey( HKEY, LPCTSTR );
+
+
+STDAPI
+AMovieSetupRegisterFilter2( const AMOVIESETUP_FILTER * const psetupdata
+ , IFilterMapper2 * pIFM2
+ , BOOL bRegister );
+
diff --git a/dshow_base/dxmperf.h b/dshow_base/dxmperf.h
new file mode 100644
index 0000000..54a2120
--- /dev/null
+++ b/dshow_base/dxmperf.h
@@ -0,0 +1,250 @@
+//------------------------------------------------------------------------------
+// File: DXMPerf.h
+//
+// Desc: Macros for DirectShow performance logging.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef _DXMPERF_H_
+#define _DXMPERF_H_
+
+#include
+#include "perflog.h"
+
+#ifdef _IA64_
+extern "C" unsigned __int64 __getReg( int whichReg );
+#pragma intrinsic(__getReg)
+#endif // _IA64_
+
+
+inline ULONGLONG _RDTSC( void ) {
+#ifdef _X86_
+ LARGE_INTEGER li;
+ __asm {
+ _emit 0x0F
+ _emit 0x31
+ mov li.LowPart,eax
+ mov li.HighPart,edx
+ }
+ return li.QuadPart;
+
+#if 0 // This isn't tested yet
+
+#elif defined (_IA64_)
+
+#define INL_REGID_APITC 3116
+ return __getReg( INL_REGID_APITC );
+
+#endif // 0
+
+#else // unsupported platform
+ // not implemented on non x86/IA64 platforms
+ return 0;
+#endif // _X86_/_IA64_
+}
+
+#define DXMPERF_VIDEOREND 0x00000001
+#define DXMPERF_AUDIOGLITCH 0x00000002
+//#define GETTIME_BIT 0x00000001
+//#define AUDIOREND_BIT 0x00000004
+//#define FRAMEDROP_BIT 0x00000008
+#define AUDIOBREAK_BIT 0x00000010
+#define DXMPERF_AUDIORECV 0x00000020
+#define DXMPERF_AUDIOSLAVE 0x00000040
+#define DXMPERF_AUDIOBREAK 0x00000080
+
+#define PERFLOG_CTOR( name, iface )
+#define PERFLOG_DTOR( name, iface )
+#define PERFLOG_DELIVER( name, source, dest, sample, pmt )
+#define PERFLOG_RECEIVE( name, source, dest, sample, pmt )
+#define PERFLOG_RUN( name, iface, time, oldstate )
+#define PERFLOG_PAUSE( name, iface, oldstate )
+#define PERFLOG_STOP( name, iface, oldstate )
+#define PERFLOG_JOINGRAPH( name, iface, graph )
+#define PERFLOG_GETBUFFER( allocator, sample )
+#define PERFLOG_RELBUFFER( allocator, sample )
+#define PERFLOG_CONNECT( connector, connectee, status, pmt )
+#define PERFLOG_RXCONNECT( connector, connectee, status, pmt )
+#define PERFLOG_DISCONNECT( disconnector, disconnectee, status )
+
+#define PERFLOG_GETTIME( clock, time ) /*{ \
+ PERFINFO_WMI_GETTIME perfData; \
+ if (NULL != g_pTraceEvent) { \
+ memset( &perfData, 0, sizeof( perfData ) ); \
+ perfData.header.Size = sizeof( perfData ); \
+ perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
+ perfData.header.Guid = GUID_GETTIME; \
+ perfData.data.cycleCounter = _RDTSC(); \
+ perfData.data.dshowClock = (ULONGLONG) (time); \
+ if (g_perfMasks[GETTIME_INDEX] & GETTIME_BIT) \
+ (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \
+ } \
+ }*/
+
+#define PERFLOG_AUDIOREND( clocktime, sampletime, psample, bytetime, cbytes ) /*{ \
+ PERFINFO_WMI_AVREND perfData; \
+ if (NULL != g_pTraceEvent) { \
+ memset( &perfData, 0, sizeof( perfData ) ); \
+ perfData.header.Size = sizeof( perfData ); \
+ perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
+ perfData.header.Guid = GUID_AUDIOREND; \
+ perfData.data.cycleCounter = _RDTSC(); \
+ perfData.data.dshowClock = (clocktime); \
+ perfData.data.sampleTime = (sampletime); \
+ if (g_perfMasks[AUDIOREND_INDEX] & AUDIOREND_BIT) \
+ (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \
+ } \
+ }*/
+
+#define PERFLOG_AUDIORECV(StreamTime,SampleStart,SampleStop,Discontinuity,Duration) \
+ if (PerflogEnableFlags & DXMPERF_AUDIORECV) { \
+ PERFINFO_WMI_AUDIORECV perfData; \
+ memset( &perfData, 0, sizeof( perfData ) ); \
+ perfData.header.Size = sizeof( perfData ); \
+ perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
+ perfData.header.Guid = GUID_AUDIORECV; \
+ perfData.data.streamTime = StreamTime; \
+ perfData.data.sampleStart = SampleStart; \
+ perfData.data.sampleStop = SampleStop; \
+ perfData.data.discontinuity = Discontinuity; \
+ perfData.data.hwduration = Duration; \
+ PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); \
+ }
+
+#define PERFLOG_AUDIOSLAVE(MasterClock,SlaveClock,ErrorAccum,LastHighErrorSeen,LastLowErrorSeen) \
+ if (PerflogEnableFlags & DXMPERF_AUDIOSLAVE) { \
+ PERFINFO_WMI_AUDIOSLAVE perfData; \
+ memset( &perfData, 0, sizeof( perfData ) ); \
+ perfData.header.Size = sizeof( perfData ); \
+ perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
+ perfData.header.Guid = GUID_AUDIOSLAVE; \
+ perfData.data.masterClock = MasterClock; \
+ perfData.data.slaveClock = SlaveClock; \
+ perfData.data.errorAccum = ErrorAccum; \
+ perfData.data.lastHighErrorSeen = LastHighErrorSeen;\
+ perfData.data.lastLowErrorSeen = LastLowErrorSeen; \
+ PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); \
+ }
+
+#define PERFLOG_AUDIOADDBREAK(IterNextWrite,OffsetNextWrite,IterWrite,OffsetWrite) \
+ if (PerflogEnableFlags & DXMPERF_AUDIOBREAK) { \
+ PERFINFO_WMI_AUDIOADDBREAK perfData; \
+ memset( &perfData, 0, sizeof( perfData ) ); \
+ perfData.header.Size = sizeof( perfData ); \
+ perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
+ perfData.header.Guid = GUID_AUDIOADDBREAK; \
+ perfData.data.iterNextWrite = IterNextWrite; \
+ perfData.data.offsetNextWrite = OffsetNextWrite; \
+ perfData.data.iterWrite = IterWrite; \
+ perfData.data.offsetWrite = OffsetWrite; \
+ PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); \
+ }
+
+#define PERFLOG_VIDEOREND( sampletime, clocktime, psample ) \
+ if (PerflogEnableFlags & DXMPERF_VIDEOREND) { \
+ PERFINFO_WMI_AVREND perfData; \
+ memset( &perfData, 0, sizeof( perfData ) ); \
+ perfData.header.Size = sizeof( perfData ); \
+ perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
+ perfData.header.Guid = GUID_VIDEOREND; \
+ perfData.data.cycleCounter = _RDTSC(); \
+ perfData.data.dshowClock = (clocktime); \
+ perfData.data.sampleTime = (sampletime); \
+ PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \
+ }
+
+#define PERFLOG_AUDIOGLITCH( instance, glitchtype, currenttime, previoustime ) \
+ if (PerflogEnableFlags & DXMPERF_AUDIOGLITCH) { \
+ PERFINFO_WMI_AUDIOGLITCH perfData; \
+ memset( &perfData, 0, sizeof( perfData ) ); \
+ perfData.header.Size = sizeof( perfData ); \
+ perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
+ perfData.header.Guid = GUID_DSOUNDGLITCH; \
+ perfData.data.cycleCounter = _RDTSC(); \
+ perfData.data.glitchType = (glitchtype); \
+ perfData.data.sampleTime = (currenttime); \
+ perfData.data.previousTime = (previoustime); \
+ perfData.data.instanceId = (instance); \
+ PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \
+ }
+
+#define PERFLOG_FRAMEDROP( sampletime, clocktime, psample, renderer ) /*{ \
+ PERFINFO_WMI_FRAMEDROP perfData; \
+ if (NULL != g_pTraceEvent) { \
+ memset( &perfData, 0, sizeof( perfData ) ); \
+ perfData.header.Size = sizeof( perfData ); \
+ perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
+ perfData.header.Guid = GUID_FRAMEDROP; \
+ perfData.data.cycleCounter = _RDTSC(); \
+ perfData.data.dshowClock = (clocktime); \
+ perfData.data.frameTime = (sampletime); \
+ if (g_perfMasks[FRAMEDROP_INDEX] & FRAMEDROP_BIT) \
+ (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \
+ } \
+ }*/
+
+/*
+#define PERFLOG_AUDIOBREAK( nextwrite, writepos, msecs ) { \
+ PERFINFO_WMI_AUDIOBREAK perfData; \
+ if (NULL != g_pTraceEvent) { \
+ memset( &perfData, 0, sizeof( perfData ) ); \
+ perfData.header.Size = sizeof( perfData ); \
+ perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
+ perfData.header.Guid = GUID_AUDIOBREAK; \
+ perfData.data.cycleCounter = _RDTSC(); \
+ perfData.data.dshowClock = (writepos); \
+ perfData.data.sampleTime = (nextwrite); \
+ perfData.data.sampleDuration = (msecs); \
+ if (g_perfMasks[AUDIOBREAK_INDEX] & AUDIOBREAK_BIT) \
+ (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \
+ } \
+ }
+*/
+
+#define PERFLOG_AUDIOBREAK( nextwrite, writepos, msecs ) \
+ if (PerflogEnableFlags & AUDIOBREAK_BIT) { \
+ PERFINFO_WMI_AUDIOBREAK perfData; \
+ memset( &perfData, 0, sizeof( perfData ) ); \
+ perfData.header.Size = sizeof( perfData ); \
+ perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \
+ perfData.header.Guid = GUID_AUDIOBREAK; \
+ perfData.data.cycleCounter = _RDTSC(); \
+ perfData.data.dshowClock = (writepos); \
+ perfData.data.sampleTime = (nextwrite); \
+ perfData.data.sampleDuration = (msecs); \
+ PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \
+ } \
+
+
+inline
+VOID PERFLOG_STREAMTRACE(
+ ULONG Level,
+ ULONG Id,
+ ULONGLONG DShowClock,
+ ULONGLONG Data1,
+ ULONGLONG Data2,
+ ULONGLONG Data3,
+ ULONGLONG Data4
+ )
+{
+ if (Level <= PerflogModuleLevel)
+ {
+ PERFINFO_WMI_STREAMTRACE perfData;
+ memset( &perfData, 0, sizeof( perfData ) );
+ perfData.header.Size = sizeof( perfData );
+ perfData.header.Flags = WNODE_FLAG_TRACED_GUID;
+ perfData.header.Guid = GUID_STREAMTRACE;
+ perfData.data.dshowClock = DShowClock;
+ perfData.data.id = Id;
+ perfData.data.data[0] = Data1;
+ perfData.data.data[1] = Data2;
+ perfData.data.data[2] = Data3;
+ perfData.data.data[3] = Data4;
+ PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData);
+ }
+}
+
+
+#endif // _DXMPERF_H_
diff --git a/dshow_base/fourcc.h b/dshow_base/fourcc.h
index ae20ab3..19c0fcd 100644
--- a/dshow_base/fourcc.h
+++ b/dshow_base/fourcc.h
@@ -1,101 +1,101 @@
-//------------------------------------------------------------------------------
-// File: FourCC.h
-//
-// Desc: DirectShow base classes.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-// FOURCCMap
-//
-// provides a mapping between old-style multimedia format DWORDs
-// and new-style GUIDs.
-//
-// A range of 4 billion GUIDs has been allocated to ensure that this
-// mapping can be done straightforwardly one-to-one in both directions.
-//
-// January 95
-
-
-#ifndef __FOURCC__
-#define __FOURCC__
-
-
-// Multimedia format types are marked with DWORDs built from four 8-bit
-// chars and known as FOURCCs. New multimedia AM_MEDIA_TYPE definitions include
-// a subtype GUID. In order to simplify the mapping, GUIDs in the range:
-// XXXXXXXX-0000-0010-8000-00AA00389B71
-// are reserved for FOURCCs.
-
-class FOURCCMap : public GUID
-{
-
-public:
- FOURCCMap();
- FOURCCMap(DWORD Fourcc);
- FOURCCMap(const GUID *);
-
-
- DWORD GetFOURCC(void);
- void SetFOURCC(DWORD fourcc);
- void SetFOURCC(const GUID *);
-
-private:
- void InitGUID();
-};
-
-#define GUID_Data2 0
-#define GUID_Data3 0x10
-#define GUID_Data4_1 0xaa000080
-#define GUID_Data4_2 0x719b3800
-
-inline void
-FOURCCMap::InitGUID() {
- Data2 = GUID_Data2;
- Data3 = GUID_Data3;
- ((DWORD *)Data4)[0] = GUID_Data4_1;
- ((DWORD *)Data4)[1] = GUID_Data4_2;
-}
-
-inline
-FOURCCMap::FOURCCMap() {
- InitGUID();
- SetFOURCC( DWORD(0));
-}
-
-inline
-FOURCCMap::FOURCCMap(DWORD fourcc)
-{
- InitGUID();
- SetFOURCC(fourcc);
-}
-
-inline
-FOURCCMap::FOURCCMap(const GUID * pGuid)
-{
- InitGUID();
- SetFOURCC(pGuid);
-}
-
-inline void
-FOURCCMap::SetFOURCC(const GUID * pGuid)
-{
- FOURCCMap * p = (FOURCCMap*) pGuid;
- SetFOURCC(p->GetFOURCC());
-}
-
-inline void
-FOURCCMap::SetFOURCC(DWORD fourcc)
-{
- Data1 = fourcc;
-}
-
-inline DWORD
-FOURCCMap::GetFOURCC(void)
-{
- return Data1;
-}
-
-#endif /* __FOURCC__ */
-
+//------------------------------------------------------------------------------
+// File: FourCC.h
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+// FOURCCMap
+//
+// provides a mapping between old-style multimedia format DWORDs
+// and new-style GUIDs.
+//
+// A range of 4 billion GUIDs has been allocated to ensure that this
+// mapping can be done straightforwardly one-to-one in both directions.
+//
+// January 95
+
+
+#ifndef __FOURCC__
+#define __FOURCC__
+
+
+// Multimedia format types are marked with DWORDs built from four 8-bit
+// chars and known as FOURCCs. New multimedia AM_MEDIA_TYPE definitions include
+// a subtype GUID. In order to simplify the mapping, GUIDs in the range:
+// XXXXXXXX-0000-0010-8000-00AA00389B71
+// are reserved for FOURCCs.
+
+class FOURCCMap : public GUID
+{
+
+public:
+ FOURCCMap();
+ FOURCCMap(DWORD Fourcc);
+ FOURCCMap(const GUID *);
+
+
+ DWORD GetFOURCC(void);
+ void SetFOURCC(DWORD fourcc);
+ void SetFOURCC(const GUID *);
+
+private:
+ void InitGUID();
+};
+
+#define GUID_Data2 0
+#define GUID_Data3 0x10
+#define GUID_Data4_1 0xaa000080
+#define GUID_Data4_2 0x719b3800
+
+inline void
+FOURCCMap::InitGUID() {
+ Data2 = GUID_Data2;
+ Data3 = GUID_Data3;
+ ((DWORD *)Data4)[0] = GUID_Data4_1;
+ ((DWORD *)Data4)[1] = GUID_Data4_2;
+}
+
+inline
+FOURCCMap::FOURCCMap() {
+ InitGUID();
+ SetFOURCC( DWORD(0));
+}
+
+inline
+FOURCCMap::FOURCCMap(DWORD fourcc)
+{
+ InitGUID();
+ SetFOURCC(fourcc);
+}
+
+inline
+FOURCCMap::FOURCCMap(const GUID * pGuid)
+{
+ InitGUID();
+ SetFOURCC(pGuid);
+}
+
+inline void
+FOURCCMap::SetFOURCC(const GUID * pGuid)
+{
+ FOURCCMap * p = (FOURCCMap*) pGuid;
+ SetFOURCC(p->GetFOURCC());
+}
+
+inline void
+FOURCCMap::SetFOURCC(DWORD fourcc)
+{
+ Data1 = fourcc;
+}
+
+inline DWORD
+FOURCCMap::GetFOURCC(void)
+{
+ return Data1;
+}
+
+#endif /* __FOURCC__ */
+
diff --git a/dshow_base/measure.h b/dshow_base/measure.h
index f90eb15..a71a075 100644
--- a/dshow_base/measure.h
+++ b/dshow_base/measure.h
@@ -1,222 +1,222 @@
-//------------------------------------------------------------------------------
-// File: Measure.h
-//
-// Desc: DirectShow base classes.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-/*
- The idea is to pepper the source code with interesting measurements and
- have the last few thousand of these recorded in a circular buffer that
- can be post-processed to give interesting numbers.
-
- WHAT THE LOG LOOKS LIKE:
-
- Time (sec) Type Delta Incident_Name
- 0.055,41 NOTE -. Incident Nine - Another note
- 0.055,42 NOTE 0.000,01 Incident Nine - Another note
- 0.055,44 NOTE 0.000,02 Incident Nine - Another note
- 0.055,45 STOP -. Incident Eight - Also random
- 0.055,47 START -. Incident Seven - Random
- 0.055,49 NOTE 0.000,05 Incident Nine - Another note
- ------- ----------------
- 0.125,60 STOP 0.000,03 Msr_Stop
- 0.125,62 START -. Msr_Start
- 0.125,63 START -. Incident Two - Start/Stop
- 0.125,65 STOP 0.000,03 Msr_Start
- 0.125,66 START -. Msr_Stop
- 0.125,68 STOP 0.000,05 Incident Two - Start/Stop
- 0.125,70 STOP 0.000,04 Msr_Stop
- 0.125,72 START -. Msr_Start
- 0.125,73 START -. Incident Two - Start/Stop
- 0.125,75 STOP 0.000,03 Msr_Start
- 0.125,77 START -. Msr_Stop
- 0.125,78 STOP 0.000,05 Incident Two - Start/Stop
- 0.125,80 STOP 0.000,03 Msr_Stop
- 0.125,81 NOTE -. Incident Three - single Note
- 0.125,83 START -. Incident Four - Start, no stop
- 0.125,85 START -. Incident Five - Single Start/Stop
- 0.125,87 STOP 0.000,02 Incident Five - Single Start/Stop
-
-Number Average StdDev Smallest Largest Incident_Name
- 10 0.000,58 0.000,10 0.000,55 0.000,85 Incident One - Note
- 50 0.000,05 0.000,00 0.000,05 0.000,05 Incident Two - Start/Stop
- 1 -. -. -. -. Incident Three - single Note
- 0 -. -. -. -. Incident Four - Start, no stop
- 1 0.000,02 -. 0.000,02 0.000,02 Incident Five - Single Start/Stop
- 0 -. -. -. -. Incident Six - zero occurrences
- 100 0.000,25 0.000,12 0.000,02 0.000,62 Incident Seven - Random
- 100 0.000,79 0.000,48 0.000,02 0.001,92 Incident Eight - Also random
- 5895 0.000,01 0.000,01 0.000,01 0.000,56 Incident Nine - Another note
- 10 0.000,03 0.000,00 0.000,03 0.000,04 Msr_Note
- 50 0.000,03 0.000,00 0.000,03 0.000,04 Msr_Start
- 50 0.000,04 0.000,03 0.000,03 0.000,31 Msr_Stop
-
- WHAT IT MEANS:
- The log shows what happened and when. Each line shows the time at which
- something happened (see WHAT YOU CODE below) what it was that happened
- and (if approporate) the time since the corresponding previous event
- (that's the delta column).
-
- The statistics show how many times each event occurred, what the average
- delta time was, also the standard deviation, largest and smalles delta.
-
- WHAT YOU CODE:
-
- Before anything else executes: - register your ids
-
- int id1 = Msr_Register("Incident One - Note");
- int id2 = Msr_Register("Incident Two - Start/Stop");
- int id3 = Msr_Register("Incident Three - single Note");
- etc.
-
- At interesting moments:
-
- // To measure a repetitive event - e.g. end of bitblt to screen
- Msr_Note(Id9); // e.g. "video frame hiting the screen NOW!"
-
- or
-
- // To measure an elapsed time e.g. time taken to decode an MPEG B-frame
- Msr_Start(Id2); // e.g. "Starting to decode MPEG B-frame"
- . . .
- MsrStop(Id2); // "Finished MPEG decode"
-
- At the end:
-
- HANDLE hFile;
- hFile = CreateFile("Perf.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
- Msr_Dump(hFile); // This writes the log out to the file
- CloseHandle(hFile);
-
- or
-
- Msr_Dump(NULL); // This writes it to DbgLog((LOG_TRACE,0, ... ));
- // but if you are writing it out to the debugger
- // then the times are probably all garbage because
- // the debugger can make things run awfully slow.
-
- A given id should be used either for start / stop or Note calls. If Notes
- are mixed in with Starts and Stops their statistics will be gibberish.
-
- If you code the calls in upper case i.e. MSR_START(idMunge); then you get
- macros which will turn into nothing unless PERF is defined.
-
- You can reset the statistical counts for a given id by calling Reset(Id).
- They are reset by default at the start.
- It logs Reset as a special incident, so you can see it in the log.
-
- The log is a circular buffer in storage (to try to minimise disk I/O).
- It overwrites the oldest entries once full. The statistics include ALL
- incidents since the last Reset, whether still visible in the log or not.
-*/
-
-#ifndef __MEASURE__
-#define __MEASURE__
-
-#ifdef PERF
-#define MSR_INIT() Msr_Init()
-#define MSR_TERMINATE() Msr_Terminate()
-#define MSR_REGISTER(a) Msr_Register(a)
-#define MSR_RESET(a) Msr_Reset(a)
-#define MSR_CONTROL(a) Msr_Control(a)
-#define MSR_START(a) Msr_Start(a)
-#define MSR_STOP(a) Msr_Stop(a)
-#define MSR_NOTE(a) Msr_Note(a)
-#define MSR_INTEGER(a,b) Msr_Integer(a,b)
-#define MSR_DUMP(a) Msr_Dump(a)
-#define MSR_DUMPSTATS(a) Msr_DumpStats(a)
-#else
-#define MSR_INIT() ((void)0)
-#define MSR_TERMINATE() ((void)0)
-#define MSR_REGISTER(a) 0
-#define MSR_RESET(a) ((void)0)
-#define MSR_CONTROL(a) ((void)0)
-#define MSR_START(a) ((void)0)
-#define MSR_STOP(a) ((void)0)
-#define MSR_NOTE(a) ((void)0)
-#define MSR_INTEGER(a,b) ((void)0)
-#define MSR_DUMP(a) ((void)0)
-#define MSR_DUMPSTATS(a) ((void)0)
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// This must be called first - (called by the DllEntry)
-
-void WINAPI Msr_Init(void);
-
-
-// Call this last to clean up (or just let it fall off the end - who cares?)
-
-void WINAPI Msr_Terminate(void);
-
-
-// Call this to get an Id for an "incident" that you can pass to Start, Stop or Note
-// everything that's logged is called an "incident".
-
-int WINAPI Msr_Register(LPTSTR Incident);
-
-
-// Reset the statistical counts for an incident
-
-void WINAPI Msr_Reset(int Id);
-
-
-// Reset all the counts for all incidents
-#define MSR_RESET_ALL 0
-#define MSR_PAUSE 1
-#define MSR_RUN 2
-
-void WINAPI Msr_Control(int iAction);
-
-
-// log the start of an operation
-
-void WINAPI Msr_Start(int Id);
-
-
-// log the end of an operation
-
-void WINAPI Msr_Stop(int Id);
-
-
-// log a one-off or repetitive operation
-
-void WINAPI Msr_Note(int Id);
-
-
-// log an integer (on which we can see statistics later)
-void WINAPI Msr_Integer(int Id, int n);
-
-
-// print out all the vaialable log (it may have wrapped) and then the statistics.
-// When the log wraps you lose log but the statistics are still complete.
-// hFIle==NULL => use DbgLog
-// otherwise hFile must have come from CreateFile or OpenFile.
-
-void WINAPI Msr_Dump(HANDLE hFile);
-
-
-// just dump the statistics - never mind the log
-
-void WINAPI Msr_DumpStats(HANDLE hFile);
-
-// Type definitions in case you want to declare a pointer to the dump functions
-// (makes it a trifle easier to do dynamic linking
-// i.e. LoadModule, GetProcAddress and call that)
-
-// Typedefs so can declare MSR_DUMPPROC *MsrDumpStats; or whatever
-typedef void WINAPI MSR_DUMPPROC(HANDLE hFile);
-typedef void WINAPI MSR_CONTROLPROC(int iAction);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // __MEASURE__
+//------------------------------------------------------------------------------
+// File: Measure.h
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+/*
+ The idea is to pepper the source code with interesting measurements and
+ have the last few thousand of these recorded in a circular buffer that
+ can be post-processed to give interesting numbers.
+
+ WHAT THE LOG LOOKS LIKE:
+
+ Time (sec) Type Delta Incident_Name
+ 0.055,41 NOTE -. Incident Nine - Another note
+ 0.055,42 NOTE 0.000,01 Incident Nine - Another note
+ 0.055,44 NOTE 0.000,02 Incident Nine - Another note
+ 0.055,45 STOP -. Incident Eight - Also random
+ 0.055,47 START -. Incident Seven - Random
+ 0.055,49 NOTE 0.000,05 Incident Nine - Another note
+ ------- ----------------
+ 0.125,60 STOP 0.000,03 Msr_Stop
+ 0.125,62 START -. Msr_Start
+ 0.125,63 START -. Incident Two - Start/Stop
+ 0.125,65 STOP 0.000,03 Msr_Start
+ 0.125,66 START -. Msr_Stop
+ 0.125,68 STOP 0.000,05 Incident Two - Start/Stop
+ 0.125,70 STOP 0.000,04 Msr_Stop
+ 0.125,72 START -. Msr_Start
+ 0.125,73 START -. Incident Two - Start/Stop
+ 0.125,75 STOP 0.000,03 Msr_Start
+ 0.125,77 START -. Msr_Stop
+ 0.125,78 STOP 0.000,05 Incident Two - Start/Stop
+ 0.125,80 STOP 0.000,03 Msr_Stop
+ 0.125,81 NOTE -. Incident Three - single Note
+ 0.125,83 START -. Incident Four - Start, no stop
+ 0.125,85 START -. Incident Five - Single Start/Stop
+ 0.125,87 STOP 0.000,02 Incident Five - Single Start/Stop
+
+Number Average StdDev Smallest Largest Incident_Name
+ 10 0.000,58 0.000,10 0.000,55 0.000,85 Incident One - Note
+ 50 0.000,05 0.000,00 0.000,05 0.000,05 Incident Two - Start/Stop
+ 1 -. -. -. -. Incident Three - single Note
+ 0 -. -. -. -. Incident Four - Start, no stop
+ 1 0.000,02 -. 0.000,02 0.000,02 Incident Five - Single Start/Stop
+ 0 -. -. -. -. Incident Six - zero occurrences
+ 100 0.000,25 0.000,12 0.000,02 0.000,62 Incident Seven - Random
+ 100 0.000,79 0.000,48 0.000,02 0.001,92 Incident Eight - Also random
+ 5895 0.000,01 0.000,01 0.000,01 0.000,56 Incident Nine - Another note
+ 10 0.000,03 0.000,00 0.000,03 0.000,04 Msr_Note
+ 50 0.000,03 0.000,00 0.000,03 0.000,04 Msr_Start
+ 50 0.000,04 0.000,03 0.000,03 0.000,31 Msr_Stop
+
+ WHAT IT MEANS:
+ The log shows what happened and when. Each line shows the time at which
+ something happened (see WHAT YOU CODE below) what it was that happened
+ and (if approporate) the time since the corresponding previous event
+ (that's the delta column).
+
+ The statistics show how many times each event occurred, what the average
+ delta time was, also the standard deviation, largest and smalles delta.
+
+ WHAT YOU CODE:
+
+ Before anything else executes: - register your ids
+
+ int id1 = Msr_Register("Incident One - Note");
+ int id2 = Msr_Register("Incident Two - Start/Stop");
+ int id3 = Msr_Register("Incident Three - single Note");
+ etc.
+
+ At interesting moments:
+
+ // To measure a repetitive event - e.g. end of bitblt to screen
+ Msr_Note(Id9); // e.g. "video frame hiting the screen NOW!"
+
+ or
+
+ // To measure an elapsed time e.g. time taken to decode an MPEG B-frame
+ Msr_Start(Id2); // e.g. "Starting to decode MPEG B-frame"
+ . . .
+ MsrStop(Id2); // "Finished MPEG decode"
+
+ At the end:
+
+ HANDLE hFile;
+ hFile = CreateFile("Perf.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+ Msr_Dump(hFile); // This writes the log out to the file
+ CloseHandle(hFile);
+
+ or
+
+ Msr_Dump(NULL); // This writes it to DbgLog((LOG_TRACE,0, ... ));
+ // but if you are writing it out to the debugger
+ // then the times are probably all garbage because
+ // the debugger can make things run awfully slow.
+
+ A given id should be used either for start / stop or Note calls. If Notes
+ are mixed in with Starts and Stops their statistics will be gibberish.
+
+ If you code the calls in upper case i.e. MSR_START(idMunge); then you get
+ macros which will turn into nothing unless PERF is defined.
+
+ You can reset the statistical counts for a given id by calling Reset(Id).
+ They are reset by default at the start.
+ It logs Reset as a special incident, so you can see it in the log.
+
+ The log is a circular buffer in storage (to try to minimise disk I/O).
+ It overwrites the oldest entries once full. The statistics include ALL
+ incidents since the last Reset, whether still visible in the log or not.
+*/
+
+#ifndef __MEASURE__
+#define __MEASURE__
+
+#ifdef PERF
+#define MSR_INIT() Msr_Init()
+#define MSR_TERMINATE() Msr_Terminate()
+#define MSR_REGISTER(a) Msr_Register(a)
+#define MSR_RESET(a) Msr_Reset(a)
+#define MSR_CONTROL(a) Msr_Control(a)
+#define MSR_START(a) Msr_Start(a)
+#define MSR_STOP(a) Msr_Stop(a)
+#define MSR_NOTE(a) Msr_Note(a)
+#define MSR_INTEGER(a,b) Msr_Integer(a,b)
+#define MSR_DUMP(a) Msr_Dump(a)
+#define MSR_DUMPSTATS(a) Msr_DumpStats(a)
+#else
+#define MSR_INIT() ((void)0)
+#define MSR_TERMINATE() ((void)0)
+#define MSR_REGISTER(a) 0
+#define MSR_RESET(a) ((void)0)
+#define MSR_CONTROL(a) ((void)0)
+#define MSR_START(a) ((void)0)
+#define MSR_STOP(a) ((void)0)
+#define MSR_NOTE(a) ((void)0)
+#define MSR_INTEGER(a,b) ((void)0)
+#define MSR_DUMP(a) ((void)0)
+#define MSR_DUMPSTATS(a) ((void)0)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// This must be called first - (called by the DllEntry)
+
+void WINAPI Msr_Init(void);
+
+
+// Call this last to clean up (or just let it fall off the end - who cares?)
+
+void WINAPI Msr_Terminate(void);
+
+
+// Call this to get an Id for an "incident" that you can pass to Start, Stop or Note
+// everything that's logged is called an "incident".
+
+int WINAPI Msr_Register(__in LPTSTR Incident);
+
+
+// Reset the statistical counts for an incident
+
+void WINAPI Msr_Reset(int Id);
+
+
+// Reset all the counts for all incidents
+#define MSR_RESET_ALL 0
+#define MSR_PAUSE 1
+#define MSR_RUN 2
+
+void WINAPI Msr_Control(int iAction);
+
+
+// log the start of an operation
+
+void WINAPI Msr_Start(int Id);
+
+
+// log the end of an operation
+
+void WINAPI Msr_Stop(int Id);
+
+
+// log a one-off or repetitive operation
+
+void WINAPI Msr_Note(int Id);
+
+
+// log an integer (on which we can see statistics later)
+void WINAPI Msr_Integer(int Id, int n);
+
+
+// print out all the vaialable log (it may have wrapped) and then the statistics.
+// When the log wraps you lose log but the statistics are still complete.
+// hFIle==NULL => use DbgLog
+// otherwise hFile must have come from CreateFile or OpenFile.
+
+void WINAPI Msr_Dump(HANDLE hFile);
+
+
+// just dump the statistics - never mind the log
+
+void WINAPI Msr_DumpStats(HANDLE hFile);
+
+// Type definitions in case you want to declare a pointer to the dump functions
+// (makes it a trifle easier to do dynamic linking
+// i.e. LoadModule, GetProcAddress and call that)
+
+// Typedefs so can declare MSR_DUMPPROC *MsrDumpStats; or whatever
+typedef void WINAPI MSR_DUMPPROC(HANDLE hFile);
+typedef void WINAPI MSR_CONTROLPROC(int iAction);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __MEASURE__
diff --git a/dshow_base/msgthrd.h b/dshow_base/msgthrd.h
index eb09be2..45adc01 100644
--- a/dshow_base/msgthrd.h
+++ b/dshow_base/msgthrd.h
@@ -1,120 +1,120 @@
-//------------------------------------------------------------------------------
-// File: MsgThrd.h
-//
-// Desc: DirectShow base classes - provides support for a worker thread
-// class to which one can asynchronously post messages.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-// Message class - really just a structure.
-//
-class CMsg {
-public:
- UINT uMsg;
- DWORD dwFlags;
- LPVOID lpParam;
- CAMEvent *pEvent;
-
- CMsg(UINT u, DWORD dw, LPVOID lp, CAMEvent *pEvnt)
- : uMsg(u), dwFlags(dw), lpParam(lp), pEvent(pEvnt) {}
-
- CMsg()
- : uMsg(0), dwFlags(0L), lpParam(NULL), pEvent(NULL) {}
-};
-
-// This is the actual thread class. It exports all the usual thread control
-// functions. The created thread is different from a normal WIN32 thread in
-// that it is prompted to perform particaular tasks by responding to messages
-// posted to its message queue.
-//
-class AM_NOVTABLE CMsgThread {
-private:
- static DWORD WINAPI DefaultThreadProc(LPVOID lpParam);
- DWORD m_ThreadId;
- HANDLE m_hThread;
-
-protected:
-
- // if you want to override GetThreadMsg to block on other things
- // as well as this queue, you need access to this
- CGenericList m_ThreadQueue;
- CCritSec m_Lock;
- HANDLE m_hSem;
- LONG m_lWaiting;
-
-public:
- CMsgThread()
- : m_ThreadId(0),
- m_hThread(NULL),
- m_lWaiting(0),
- m_hSem(NULL),
- // make a list with a cache of 5 items
- m_ThreadQueue(NAME("MsgThread list"), 5)
- {
- }
-
- ~CMsgThread();
- // override this if you want to block on other things as well
- // as the message loop
- void virtual GetThreadMsg(CMsg *msg);
-
- // override this if you want to do something on thread startup
- virtual void OnThreadInit() {
- };
-
- BOOL CreateThread();
-
- BOOL WaitForThreadExit(LPDWORD lpdwExitCode) {
- if (m_hThread != NULL) {
- WaitForSingleObject(m_hThread, INFINITE);
- return GetExitCodeThread(m_hThread, lpdwExitCode);
- }
- return FALSE;
- }
-
- DWORD ResumeThread() {
- return ::ResumeThread(m_hThread);
- }
-
- DWORD SuspendThread() {
- return ::SuspendThread(m_hThread);
- }
-
- int GetThreadPriority() {
- return ::GetThreadPriority(m_hThread);
- }
-
- BOOL SetThreadPriority(int nPriority) {
- return ::SetThreadPriority(m_hThread, nPriority);
- }
-
- HANDLE GetThreadHandle() {
- return m_hThread;
- }
-
- DWORD GetThreadId() {
- return m_ThreadId;
- }
-
-
- void PutThreadMsg(UINT uMsg, DWORD dwMsgFlags,
- LPVOID lpMsgParam, CAMEvent *pEvent = NULL) {
- CAutoLock lck(&m_Lock);
- CMsg* pMsg = new CMsg(uMsg, dwMsgFlags, lpMsgParam, pEvent);
- m_ThreadQueue.AddTail(pMsg);
- if (m_lWaiting != 0) {
- ReleaseSemaphore(m_hSem, m_lWaiting, 0);
- m_lWaiting = 0;
- }
- }
-
- // This is the function prototype of the function that the client
- // supplies. It is always called on the created thread, never on
- // the creator thread.
- //
- virtual LRESULT ThreadMessageProc(
- UINT uMsg, DWORD dwFlags, LPVOID lpParam, CAMEvent *pEvent) = 0;
-};
-
+//------------------------------------------------------------------------------
+// File: MsgThrd.h
+//
+// Desc: DirectShow base classes - provides support for a worker thread
+// class to which one can asynchronously post messages.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+// Message class - really just a structure.
+//
+class CMsg {
+public:
+ UINT uMsg;
+ DWORD dwFlags;
+ LPVOID lpParam;
+ CAMEvent *pEvent;
+
+ CMsg(UINT u, DWORD dw, __inout_opt LPVOID lp, __in_opt CAMEvent *pEvnt)
+ : uMsg(u), dwFlags(dw), lpParam(lp), pEvent(pEvnt) {}
+
+ CMsg()
+ : uMsg(0), dwFlags(0L), lpParam(NULL), pEvent(NULL) {}
+};
+
+// This is the actual thread class. It exports all the usual thread control
+// functions. The created thread is different from a normal WIN32 thread in
+// that it is prompted to perform particaular tasks by responding to messages
+// posted to its message queue.
+//
+class AM_NOVTABLE CMsgThread {
+private:
+ static DWORD WINAPI DefaultThreadProc(__inout LPVOID lpParam);
+ DWORD m_ThreadId;
+ HANDLE m_hThread;
+
+protected:
+
+ // if you want to override GetThreadMsg to block on other things
+ // as well as this queue, you need access to this
+ CGenericList m_ThreadQueue;
+ CCritSec m_Lock;
+ HANDLE m_hSem;
+ LONG m_lWaiting;
+
+public:
+ CMsgThread()
+ : m_ThreadId(0),
+ m_hThread(NULL),
+ m_lWaiting(0),
+ m_hSem(NULL),
+ // make a list with a cache of 5 items
+ m_ThreadQueue(NAME("MsgThread list"), 5)
+ {
+ }
+
+ ~CMsgThread();
+ // override this if you want to block on other things as well
+ // as the message loop
+ void virtual GetThreadMsg(__out CMsg *msg);
+
+ // override this if you want to do something on thread startup
+ virtual void OnThreadInit() {
+ };
+
+ BOOL CreateThread();
+
+ BOOL WaitForThreadExit(__out LPDWORD lpdwExitCode) {
+ if (m_hThread != NULL) {
+ WaitForSingleObject(m_hThread, INFINITE);
+ return GetExitCodeThread(m_hThread, lpdwExitCode);
+ }
+ return FALSE;
+ }
+
+ DWORD ResumeThread() {
+ return ::ResumeThread(m_hThread);
+ }
+
+ DWORD SuspendThread() {
+ return ::SuspendThread(m_hThread);
+ }
+
+ int GetThreadPriority() {
+ return ::GetThreadPriority(m_hThread);
+ }
+
+ BOOL SetThreadPriority(int nPriority) {
+ return ::SetThreadPriority(m_hThread, nPriority);
+ }
+
+ HANDLE GetThreadHandle() {
+ return m_hThread;
+ }
+
+ DWORD GetThreadId() {
+ return m_ThreadId;
+ }
+
+
+ void PutThreadMsg(UINT uMsg, DWORD dwMsgFlags,
+ __in_opt LPVOID lpMsgParam, __in_opt CAMEvent *pEvent = NULL) {
+ CAutoLock lck(&m_Lock);
+ CMsg* pMsg = new CMsg(uMsg, dwMsgFlags, lpMsgParam, pEvent);
+ m_ThreadQueue.AddTail(pMsg);
+ if (m_lWaiting != 0) {
+ ReleaseSemaphore(m_hSem, m_lWaiting, 0);
+ m_lWaiting = 0;
+ }
+ }
+
+ // This is the function prototype of the function that the client
+ // supplies. It is always called on the created thread, never on
+ // the creator thread.
+ //
+ virtual LRESULT ThreadMessageProc(
+ UINT uMsg, DWORD dwFlags, __inout_opt LPVOID lpParam, __in_opt CAMEvent *pEvent) = 0;
+};
+
diff --git a/dshow_base/mtype.cpp b/dshow_base/mtype.cpp
new file mode 100644
index 0000000..fffbcf7
--- /dev/null
+++ b/dshow_base/mtype.cpp
@@ -0,0 +1,478 @@
+//------------------------------------------------------------------------------
+// File: MType.cpp
+//
+// Desc: DirectShow base classes - implements a class that holds and
+// manages media type information.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+// helper class that derived pin objects can use to compare media
+// types etc. Has same data members as the struct AM_MEDIA_TYPE defined
+// in the streams IDL file, but also has (non-virtual) functions
+
+#include
+#include
+
+CMediaType::~CMediaType(){
+ FreeMediaType(*this);
+}
+
+
+CMediaType::CMediaType()
+{
+ InitMediaType();
+}
+
+
+CMediaType::CMediaType(const GUID * type)
+{
+ InitMediaType();
+ majortype = *type;
+}
+
+
+// copy constructor does a deep copy of the format block
+
+CMediaType::CMediaType(const AM_MEDIA_TYPE& rt, __out_opt HRESULT* phr)
+{
+ HRESULT hr = CopyMediaType(this, &rt);
+ if (FAILED(hr) && (NULL != phr)) {
+ *phr = hr;
+ }
+}
+
+
+CMediaType::CMediaType(const CMediaType& rt, __out_opt HRESULT* phr)
+{
+ HRESULT hr = CopyMediaType(this, &rt);
+ if (FAILED(hr) && (NULL != phr)) {
+ *phr = hr;
+ }
+}
+
+
+// this class inherits publicly from AM_MEDIA_TYPE so the compiler could generate
+// the following assignment operator itself, however it could introduce some
+// memory conflicts and leaks in the process because the structure contains
+// a dynamically allocated block (pbFormat) which it will not copy correctly
+
+CMediaType&
+CMediaType::operator=(const AM_MEDIA_TYPE& rt)
+{
+ Set(rt);
+ return *this;
+}
+
+CMediaType&
+CMediaType::operator=(const CMediaType& rt)
+{
+ *this = (AM_MEDIA_TYPE &) rt;
+ return *this;
+}
+
+BOOL
+CMediaType::operator == (const CMediaType& rt) const
+{
+ // I don't believe we need to check sample size or
+ // temporal compression flags, since I think these must
+ // be represented in the type, subtype and format somehow. They
+ // are pulled out as separate flags so that people who don't understand
+ // the particular format representation can still see them, but
+ // they should duplicate information in the format block.
+
+ return ((IsEqualGUID(majortype,rt.majortype) == TRUE) &&
+ (IsEqualGUID(subtype,rt.subtype) == TRUE) &&
+ (IsEqualGUID(formattype,rt.formattype) == TRUE) &&
+ (cbFormat == rt.cbFormat) &&
+ ( (cbFormat == 0) ||
+ pbFormat != NULL && rt.pbFormat != NULL &&
+ (memcmp(pbFormat, rt.pbFormat, cbFormat) == 0)));
+}
+
+
+BOOL
+CMediaType::operator != (const CMediaType& rt) const
+{
+ /* Check to see if they are equal */
+
+ if (*this == rt) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+HRESULT
+CMediaType::Set(const CMediaType& rt)
+{
+ return Set((AM_MEDIA_TYPE &) rt);
+}
+
+
+HRESULT
+CMediaType::Set(const AM_MEDIA_TYPE& rt)
+{
+ if (&rt != this) {
+ FreeMediaType(*this);
+ HRESULT hr = CopyMediaType(this, &rt);
+ if (FAILED(hr)) {
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ return S_OK;
+}
+
+
+BOOL
+CMediaType::IsValid() const
+{
+ return (!IsEqualGUID(majortype,GUID_NULL));
+}
+
+
+void
+CMediaType::SetType(const GUID* ptype)
+{
+ majortype = *ptype;
+}
+
+
+void
+CMediaType::SetSubtype(const GUID* ptype)
+{
+ subtype = *ptype;
+}
+
+
+ULONG
+CMediaType::GetSampleSize() const {
+ if (IsFixedSize()) {
+ return lSampleSize;
+ } else {
+ return 0;
+ }
+}
+
+
+void
+CMediaType::SetSampleSize(ULONG sz) {
+ if (sz == 0) {
+ SetVariableSize();
+ } else {
+ bFixedSizeSamples = TRUE;
+ lSampleSize = sz;
+ }
+}
+
+
+void
+CMediaType::SetVariableSize() {
+ bFixedSizeSamples = FALSE;
+}
+
+
+void
+CMediaType::SetTemporalCompression(BOOL bCompressed) {
+ bTemporalCompression = bCompressed;
+}
+
+BOOL
+CMediaType::SetFormat(__in_bcount(cb) BYTE * pformat, ULONG cb)
+{
+ if (NULL == AllocFormatBuffer(cb))
+ return(FALSE);
+
+ ASSERT(pbFormat);
+ memcpy(pbFormat, pformat, cb);
+ return(TRUE);
+}
+
+
+// set the type of the media type format block, this type defines what you
+// will actually find in the format pointer. For example FORMAT_VideoInfo or
+// FORMAT_WaveFormatEx. In the future this may be an interface pointer to a
+// property set. Before sending out media types this should be filled in.
+
+void
+CMediaType::SetFormatType(const GUID *pformattype)
+{
+ formattype = *pformattype;
+}
+
+
+// reset the format buffer
+
+void CMediaType::ResetFormatBuffer()
+{
+ if (cbFormat) {
+ CoTaskMemFree((PVOID)pbFormat);
+ }
+ cbFormat = 0;
+ pbFormat = NULL;
+}
+
+
+// allocate length bytes for the format and return a read/write pointer
+// If we cannot allocate the new block of memory we return NULL leaving
+// the original block of memory untouched (as does ReallocFormatBuffer)
+
+BYTE*
+CMediaType::AllocFormatBuffer(ULONG length)
+{
+ ASSERT(length);
+
+ // do the types have the same buffer size
+
+ if (cbFormat == length) {
+ return pbFormat;
+ }
+
+ // allocate the new format buffer
+
+ BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);
+ if (pNewFormat == NULL) {
+ if (length <= cbFormat) return pbFormat; //reuse the old block anyway.
+ return NULL;
+ }
+
+ // delete the old format
+
+ if (cbFormat != 0) {
+ ASSERT(pbFormat);
+ CoTaskMemFree((PVOID)pbFormat);
+ }
+
+ cbFormat = length;
+ pbFormat = pNewFormat;
+ return pbFormat;
+}
+
+
+// reallocate length bytes for the format and return a read/write pointer
+// to it. We keep as much information as we can given the new buffer size
+// if this fails the original format buffer is left untouched. The caller
+// is responsible for ensuring the size of memory required is non zero
+
+BYTE*
+CMediaType::ReallocFormatBuffer(ULONG length)
+{
+ ASSERT(length);
+
+ // do the types have the same buffer size
+
+ if (cbFormat == length) {
+ return pbFormat;
+ }
+
+ // allocate the new format buffer
+
+ BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);
+ if (pNewFormat == NULL) {
+ if (length <= cbFormat) return pbFormat; //reuse the old block anyway.
+ return NULL;
+ }
+
+ // copy any previous format (or part of if new is smaller)
+ // delete the old format and replace with the new one
+
+ if (cbFormat != 0) {
+ ASSERT(pbFormat);
+ memcpy(pNewFormat,pbFormat,min(length,cbFormat));
+ CoTaskMemFree((PVOID)pbFormat);
+ }
+
+ cbFormat = length;
+ pbFormat = pNewFormat;
+ return pNewFormat;
+}
+
+// initialise a media type structure
+
+void CMediaType::InitMediaType()
+{
+ ZeroMemory((PVOID)this, sizeof(*this));
+ lSampleSize = 1;
+ bFixedSizeSamples = TRUE;
+}
+
+
+// a partially specified media type can be passed to IPin::Connect
+// as a constraint on the media type used in the connection.
+// the type, subtype or format type can be null.
+BOOL
+CMediaType::IsPartiallySpecified(void) const
+{
+ if ((majortype == GUID_NULL) ||
+ (formattype == GUID_NULL)) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+BOOL
+CMediaType::MatchesPartial(const CMediaType* ppartial) const
+{
+ if ((ppartial->majortype != GUID_NULL) &&
+ (majortype != ppartial->majortype)) {
+ return FALSE;
+ }
+ if ((ppartial->subtype != GUID_NULL) &&
+ (subtype != ppartial->subtype)) {
+ return FALSE;
+ }
+
+ if (ppartial->formattype != GUID_NULL) {
+ // if the format block is specified then it must match exactly
+ if (formattype != ppartial->formattype) {
+ return FALSE;
+ }
+ if (cbFormat != ppartial->cbFormat) {
+ return FALSE;
+ }
+ if ((cbFormat != 0) &&
+ (memcmp(pbFormat, ppartial->pbFormat, cbFormat) != 0)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+
+}
+
+
+
+// general purpose function to delete a heap allocated AM_MEDIA_TYPE structure
+// which is useful when calling IEnumMediaTypes::Next as the interface
+// implementation allocates the structures which you must later delete
+// the format block may also be a pointer to an interface to release
+
+void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt)
+{
+ // allow NULL pointers for coding simplicity
+
+ if (pmt == NULL) {
+ return;
+ }
+
+ FreeMediaType(*pmt);
+ CoTaskMemFree((PVOID)pmt);
+}
+
+
+// this also comes in useful when using the IEnumMediaTypes interface so
+// that you can copy a media type, you can do nearly the same by creating
+// a CMediaType object but as soon as it goes out of scope the destructor
+// will delete the memory it allocated (this takes a copy of the memory)
+
+AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc)
+{
+ ASSERT(pSrc);
+
+ // Allocate a block of memory for the media type
+
+ AM_MEDIA_TYPE *pMediaType =
+ (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
+
+ if (pMediaType == NULL) {
+ return NULL;
+ }
+ // Copy the variable length format block
+
+ HRESULT hr = CopyMediaType(pMediaType,pSrc);
+ if (FAILED(hr)) {
+ CoTaskMemFree((PVOID)pMediaType);
+ return NULL;
+ }
+
+ return pMediaType;
+}
+
+
+// Copy 1 media type to another
+
+HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource)
+{
+ // We'll leak if we copy onto one that already exists - there's one
+ // case we can check like that - copying to itself.
+ ASSERT(pmtSource != pmtTarget);
+ *pmtTarget = *pmtSource;
+ if (pmtSource->cbFormat != 0) {
+ ASSERT(pmtSource->pbFormat != NULL);
+ pmtTarget->pbFormat = (PBYTE)CoTaskMemAlloc(pmtSource->cbFormat);
+ if (pmtTarget->pbFormat == NULL) {
+ pmtTarget->cbFormat = 0;
+ return E_OUTOFMEMORY;
+ } else {
+ CopyMemory((PVOID)pmtTarget->pbFormat, (PVOID)pmtSource->pbFormat,
+ pmtTarget->cbFormat);
+ }
+ }
+ if (pmtTarget->pUnk != NULL) {
+ pmtTarget->pUnk->AddRef();
+ }
+
+ return S_OK;
+}
+
+// Free an existing media type (ie free resources it holds)
+
+void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt)
+{
+ if (mt.cbFormat != 0) {
+ CoTaskMemFree((PVOID)mt.pbFormat);
+
+ // Strictly unnecessary but tidier
+ mt.cbFormat = 0;
+ mt.pbFormat = NULL;
+ }
+ if (mt.pUnk != NULL) {
+ mt.pUnk->Release();
+ mt.pUnk = NULL;
+ }
+}
+
+// Initialize a media type from a WAVEFORMATEX
+
+STDAPI CreateAudioMediaType(
+ const WAVEFORMATEX *pwfx,
+ __out AM_MEDIA_TYPE *pmt,
+ BOOL bSetFormat
+)
+{
+ pmt->majortype = MEDIATYPE_Audio;
+ if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
+ pmt->subtype = ((PWAVEFORMATEXTENSIBLE)pwfx)->SubFormat;
+ } else {
+ pmt->subtype = FOURCCMap(pwfx->wFormatTag);
+ }
+ pmt->formattype = FORMAT_WaveFormatEx;
+ pmt->bFixedSizeSamples = TRUE;
+ pmt->bTemporalCompression = FALSE;
+ pmt->lSampleSize = pwfx->nBlockAlign;
+ pmt->pUnk = NULL;
+ if (bSetFormat) {
+ if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {
+ pmt->cbFormat = sizeof(WAVEFORMATEX);
+ } else {
+ pmt->cbFormat = sizeof(WAVEFORMATEX) + pwfx->cbSize;
+ }
+ pmt->pbFormat = (PBYTE)CoTaskMemAlloc(pmt->cbFormat);
+ if (pmt->pbFormat == NULL) {
+ return E_OUTOFMEMORY;
+ }
+ if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {
+ CopyMemory(pmt->pbFormat, pwfx, sizeof(PCMWAVEFORMAT));
+ ((WAVEFORMATEX *)pmt->pbFormat)->cbSize = 0;
+ } else {
+ CopyMemory(pmt->pbFormat, pwfx, pmt->cbFormat);
+ }
+ }
+ return S_OK;
+}
+
+// eliminate very many spurious warnings from MS compiler
+#pragma warning(disable:4514)
diff --git a/dshow_base/mtype.h b/dshow_base/mtype.h
index 41d5829..fc2fe53 100644
--- a/dshow_base/mtype.h
+++ b/dshow_base/mtype.h
@@ -1,89 +1,89 @@
-//------------------------------------------------------------------------------
-// File: MtType.h
-//
-// Desc: DirectShow base classes - defines a class that holds and manages
-// media type information.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __MTYPE__
-#define __MTYPE__
-
-/* Helper class that derived pin objects can use to compare media
- types etc. Has same data members as the struct AM_MEDIA_TYPE defined
- in the streams IDL file, but also has (non-virtual) functions */
-
-class CMediaType : public _AMMediaType {
-
-public:
-
- ~CMediaType();
- CMediaType();
- CMediaType(const GUID * majortype);
- CMediaType(const AM_MEDIA_TYPE&, HRESULT* phr = NULL);
- CMediaType(const CMediaType&, HRESULT* phr = NULL);
-
- CMediaType& operator=(const CMediaType&);
- CMediaType& operator=(const AM_MEDIA_TYPE&);
-
- BOOL operator == (const CMediaType&) const;
- BOOL operator != (const CMediaType&) const;
-
- HRESULT Set(const CMediaType& rt);
- HRESULT Set(const AM_MEDIA_TYPE& rt);
-
- BOOL IsValid() const;
-
- const GUID *Type() const { return &majortype;} ;
- void SetType(const GUID *);
- const GUID *Subtype() const { return &subtype;} ;
- void SetSubtype(const GUID *);
-
- BOOL IsFixedSize() const {return bFixedSizeSamples; };
- BOOL IsTemporalCompressed() const {return bTemporalCompression; };
- ULONG GetSampleSize() const;
-
- void SetSampleSize(ULONG sz);
- void SetVariableSize();
- void SetTemporalCompression(BOOL bCompressed);
-
- // read/write pointer to format - can't change length without
- // calling SetFormat, AllocFormatBuffer or ReallocFormatBuffer
-
- BYTE* Format() const {return pbFormat; };
- ULONG FormatLength() const { return cbFormat; };
-
- void SetFormatType(const GUID *);
- const GUID *FormatType() const {return &formattype; };
- BOOL SetFormat(BYTE *pFormat, ULONG length);
- void ResetFormatBuffer();
- BYTE* AllocFormatBuffer(ULONG length);
- BYTE* ReallocFormatBuffer(ULONG length);
-
- void InitMediaType();
-
- BOOL MatchesPartial(const CMediaType* ppartial) const;
- BOOL IsPartiallySpecified(void) const;
-};
-
-
-/* General purpose functions to copy and delete a task allocated AM_MEDIA_TYPE
- structure which is useful when using the IEnumMediaFormats interface as
- the implementation allocates the structures which you must later delete */
-
-void WINAPI DeleteMediaType(AM_MEDIA_TYPE *pmt);
-AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc);
-HRESULT WINAPI CopyMediaType(AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource);
-void WINAPI FreeMediaType(AM_MEDIA_TYPE& mt);
-
-// Initialize a media type from a WAVEFORMATEX
-
-STDAPI CreateAudioMediaType(
- const WAVEFORMATEX *pwfx,
- AM_MEDIA_TYPE *pmt,
- BOOL bSetFormat);
-
-#endif /* __MTYPE__ */
-
+//------------------------------------------------------------------------------
+// File: MtType.h
+//
+// Desc: DirectShow base classes - defines a class that holds and manages
+// media type information.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __MTYPE__
+#define __MTYPE__
+
+/* Helper class that derived pin objects can use to compare media
+ types etc. Has same data members as the struct AM_MEDIA_TYPE defined
+ in the streams IDL file, but also has (non-virtual) functions */
+
+class CMediaType : public _AMMediaType {
+
+public:
+
+ ~CMediaType();
+ CMediaType();
+ CMediaType(const GUID * majortype);
+ CMediaType(const AM_MEDIA_TYPE&, __out_opt HRESULT* phr = NULL);
+ CMediaType(const CMediaType&, __out_opt HRESULT* phr = NULL);
+
+ CMediaType& operator=(const CMediaType&);
+ CMediaType& operator=(const AM_MEDIA_TYPE&);
+
+ BOOL operator == (const CMediaType&) const;
+ BOOL operator != (const CMediaType&) const;
+
+ HRESULT Set(const CMediaType& rt);
+ HRESULT Set(const AM_MEDIA_TYPE& rt);
+
+ BOOL IsValid() const;
+
+ const GUID *Type() const { return &majortype;} ;
+ void SetType(const GUID *);
+ const GUID *Subtype() const { return &subtype;} ;
+ void SetSubtype(const GUID *);
+
+ BOOL IsFixedSize() const {return bFixedSizeSamples; };
+ BOOL IsTemporalCompressed() const {return bTemporalCompression; };
+ ULONG GetSampleSize() const;
+
+ void SetSampleSize(ULONG sz);
+ void SetVariableSize();
+ void SetTemporalCompression(BOOL bCompressed);
+
+ // read/write pointer to format - can't change length without
+ // calling SetFormat, AllocFormatBuffer or ReallocFormatBuffer
+
+ BYTE* Format() const {return pbFormat; };
+ ULONG FormatLength() const { return cbFormat; };
+
+ void SetFormatType(const GUID *);
+ const GUID *FormatType() const {return &formattype; };
+ BOOL SetFormat(__in_bcount(length) BYTE *pFormat, ULONG length);
+ void ResetFormatBuffer();
+ BYTE* AllocFormatBuffer(ULONG length);
+ BYTE* ReallocFormatBuffer(ULONG length);
+
+ void InitMediaType();
+
+ BOOL MatchesPartial(const CMediaType* ppartial) const;
+ BOOL IsPartiallySpecified(void) const;
+};
+
+
+/* General purpose functions to copy and delete a task allocated AM_MEDIA_TYPE
+ structure which is useful when using the IEnumMediaFormats interface as
+ the implementation allocates the structures which you must later delete */
+
+void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt);
+AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc);
+HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource);
+void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt);
+
+// Initialize a media type from a WAVEFORMATEX
+
+STDAPI CreateAudioMediaType(
+ const WAVEFORMATEX *pwfx,
+ __out AM_MEDIA_TYPE *pmt,
+ BOOL bSetFormat);
+
+#endif /* __MTYPE__ */
+
diff --git a/dshow_base/outputq.cpp b/dshow_base/outputq.cpp
new file mode 100644
index 0000000..d3ab617
--- /dev/null
+++ b/dshow_base/outputq.cpp
@@ -0,0 +1,801 @@
+//------------------------------------------------------------------------------
+// File: OutputQ.cpp
+//
+// Desc: DirectShow base classes - implements COutputQueue class used by an
+// output pin which may sometimes want to queue output samples on a
+// separate thread and sometimes call Receive() directly on the input
+// pin.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+
+
+//
+// COutputQueue Constructor :
+//
+// Determines if a thread is to be created and creates resources
+//
+// pInputPin - the downstream input pin we're queueing samples to
+//
+// phr - changed to a failure code if this function fails
+// (otherwise unchanges)
+//
+// bAuto - Ask pInputPin if it can block in Receive by calling
+// its ReceiveCanBlock method and create a thread if
+// it can block, otherwise not.
+//
+// bQueue - if bAuto == FALSE then we create a thread if and only
+// if bQueue == TRUE
+//
+// lBatchSize - work in batches of lBatchSize
+//
+// bBatchEact - Use exact batch sizes so don't send until the
+// batch is full or SendAnyway() is called
+//
+// lListSize - If we create a thread make the list of samples queued
+// to the thread have this size cache
+//
+// dwPriority - If we create a thread set its priority to this
+//
+COutputQueue::COutputQueue(
+ IPin *pInputPin, // Pin to send stuff to
+ __inout HRESULT *phr, // 'Return code'
+ BOOL bAuto, // Ask pin if queue or not
+ BOOL bQueue, // Send through queue
+ LONG lBatchSize, // Batch
+ BOOL bBatchExact, // Batch exactly to BatchSize
+ LONG lListSize,
+ DWORD dwPriority,
+ bool bFlushingOpt // flushing optimization
+ ) : m_lBatchSize(lBatchSize),
+ m_bBatchExact(bBatchExact && (lBatchSize > 1)),
+ m_hThread(NULL),
+ m_hSem(NULL),
+ m_List(NULL),
+ m_pPin(pInputPin),
+ m_ppSamples(NULL),
+ m_lWaiting(0),
+ m_evFlushComplete(FALSE, phr),
+ m_pInputPin(NULL),
+ m_bSendAnyway(FALSE),
+ m_nBatched(0),
+ m_bFlushing(FALSE),
+ m_bFlushed(TRUE),
+ m_bFlushingOpt(bFlushingOpt),
+ m_bTerminate(FALSE),
+ m_hEventPop(NULL),
+ m_hr(S_OK)
+{
+ ASSERT(m_lBatchSize > 0);
+
+
+ if (FAILED(*phr)) {
+ return;
+ }
+
+ // Check the input pin is OK and cache its IMemInputPin interface
+
+ *phr = pInputPin->QueryInterface(IID_IMemInputPin, (void **)&m_pInputPin);
+ if (FAILED(*phr)) {
+ return;
+ }
+
+ // See if we should ask the downstream pin
+
+ if (bAuto) {
+ HRESULT hr = m_pInputPin->ReceiveCanBlock();
+ if (SUCCEEDED(hr)) {
+ bQueue = hr == S_OK;
+ }
+ }
+
+ // Create our sample batch
+
+ m_ppSamples = new PMEDIASAMPLE[m_lBatchSize];
+ if (m_ppSamples == NULL) {
+ *phr = E_OUTOFMEMORY;
+ return;
+ }
+
+ // If we're queueing allocate resources
+
+ if (bQueue) {
+ DbgLog((LOG_TRACE, 2, TEXT("Creating thread for output pin")));
+ m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
+ if (m_hSem == NULL) {
+ DWORD dwError = GetLastError();
+ *phr = AmHresultFromWin32(dwError);
+ return;
+ }
+ m_List = new CSampleList(NAME("Sample Queue List"),
+ lListSize,
+ FALSE // No lock
+ );
+ if (m_List == NULL) {
+ *phr = E_OUTOFMEMORY;
+ return;
+ }
+
+
+ DWORD dwThreadId;
+ m_hThread = CreateThread(NULL,
+ 0,
+ InitialThreadProc,
+ (LPVOID)this,
+ 0,
+ &dwThreadId);
+ if (m_hThread == NULL) {
+ DWORD dwError = GetLastError();
+ *phr = AmHresultFromWin32(dwError);
+ return;
+ }
+ SetThreadPriority(m_hThread, dwPriority);
+ } else {
+ DbgLog((LOG_TRACE, 2, TEXT("Calling input pin directly - no thread")));
+ }
+}
+
+//
+// COutputQueuee Destructor :
+//
+// Free all resources -
+//
+// Thread,
+// Batched samples
+//
+COutputQueue::~COutputQueue()
+{
+ DbgLog((LOG_TRACE, 3, TEXT("COutputQueue::~COutputQueue")));
+ /* Free our pointer */
+ if (m_pInputPin != NULL) {
+ m_pInputPin->Release();
+ }
+ if (m_hThread != NULL) {
+ {
+ CAutoLock lck(this);
+ m_bTerminate = TRUE;
+ m_hr = S_FALSE;
+ NotifyThread();
+ }
+ DbgWaitForSingleObject(m_hThread);
+ EXECUTE_ASSERT(CloseHandle(m_hThread));
+
+ // The thread frees the samples when asked to terminate
+
+ ASSERT(m_List->GetCount() == 0);
+ delete m_List;
+ } else {
+ FreeSamples();
+ }
+ if (m_hSem != NULL) {
+ EXECUTE_ASSERT(CloseHandle(m_hSem));
+ }
+ delete [] m_ppSamples;
+}
+
+//
+// Call the real thread proc as a member function
+//
+DWORD WINAPI COutputQueue::InitialThreadProc(__in LPVOID pv)
+{
+ HRESULT hrCoInit = CAMThread::CoInitializeHelper();
+
+ COutputQueue *pSampleQueue = (COutputQueue *)pv;
+ DWORD dwReturn = pSampleQueue->ThreadProc();
+
+ if(hrCoInit == S_OK) {
+ CoUninitialize();
+ }
+
+ return dwReturn;
+}
+
+//
+// Thread sending the samples downstream :
+//
+// When there is nothing to do the thread sets m_lWaiting (while
+// holding the critical section) and then waits for m_hSem to be
+// set (not holding the critical section)
+//
+DWORD COutputQueue::ThreadProc()
+{
+ while (TRUE) {
+ BOOL bWait = FALSE;
+ IMediaSample *pSample;
+ LONG lNumberToSend; // Local copy
+ NewSegmentPacket* ppacket;
+
+ //
+ // Get a batch of samples and send it if possible
+ // In any case exit the loop if there is a control action
+ // requested
+ //
+ {
+ CAutoLock lck(this);
+ while (TRUE) {
+
+ if (m_bTerminate) {
+ FreeSamples();
+ return 0;
+ }
+ if (m_bFlushing) {
+ FreeSamples();
+ SetEvent(m_evFlushComplete);
+ }
+
+ // Get a sample off the list
+
+ pSample = m_List->RemoveHead();
+ // inform derived class we took something off the queue
+ if (m_hEventPop) {
+ //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
+ SetEvent(m_hEventPop);
+ }
+
+ if (pSample != NULL &&
+ !IsSpecialSample(pSample)) {
+
+ // If its just a regular sample just add it to the batch
+ // and exit the loop if the batch is full
+
+ m_ppSamples[m_nBatched++] = pSample;
+ if (m_nBatched == m_lBatchSize) {
+ break;
+ }
+ } else {
+
+ // If there was nothing in the queue and there's nothing
+ // to send (either because there's nothing or the batch
+ // isn't full) then prepare to wait
+
+ if (pSample == NULL &&
+ (m_bBatchExact || m_nBatched == 0)) {
+
+ // Tell other thread to set the event when there's
+ // something do to
+
+ ASSERT(m_lWaiting == 0);
+ m_lWaiting++;
+ bWait = TRUE;
+ } else {
+
+ // We break out of the loop on SEND_PACKET unless
+ // there's nothing to send
+
+ if (pSample == SEND_PACKET && m_nBatched == 0) {
+ continue;
+ }
+
+ if (pSample == NEW_SEGMENT) {
+ // now we need the parameters - we are
+ // guaranteed that the next packet contains them
+ ppacket = (NewSegmentPacket *) m_List->RemoveHead();
+ // we took something off the queue
+ if (m_hEventPop) {
+ //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
+ SetEvent(m_hEventPop);
+ }
+
+ ASSERT(ppacket);
+ }
+ // EOS_PACKET falls through here and we exit the loop
+ // In this way it acts like SEND_PACKET
+ }
+ break;
+ }
+ }
+ if (!bWait) {
+ // We look at m_nBatched from the client side so keep
+ // it up to date inside the critical section
+ lNumberToSend = m_nBatched; // Local copy
+ m_nBatched = 0;
+ }
+ }
+
+ // Wait for some more data
+
+ if (bWait) {
+ DbgWaitForSingleObject(m_hSem);
+ continue;
+ }
+
+
+
+ // OK - send it if there's anything to send
+ // We DON'T check m_bBatchExact here because either we've got
+ // a full batch or we dropped through because we got
+ // SEND_PACKET or EOS_PACKET - both of which imply we should
+ // flush our batch
+
+ if (lNumberToSend != 0) {
+ long nProcessed;
+ if (m_hr == S_OK) {
+ ASSERT(!m_bFlushed);
+ HRESULT hr = m_pInputPin->ReceiveMultiple(m_ppSamples,
+ lNumberToSend,
+ &nProcessed);
+ /* Don't overwrite a flushing state HRESULT */
+ CAutoLock lck(this);
+ if (m_hr == S_OK) {
+ m_hr = hr;
+ }
+ ASSERT(!m_bFlushed);
+ }
+ while (lNumberToSend != 0) {
+ m_ppSamples[--lNumberToSend]->Release();
+ }
+ if (m_hr != S_OK) {
+
+ // In any case wait for more data - S_OK just
+ // means there wasn't an error
+
+ DbgLog((LOG_ERROR, 2, TEXT("ReceiveMultiple returned %8.8X"),
+ m_hr));
+ }
+ }
+
+ // Check for end of stream
+
+ if (pSample == EOS_PACKET) {
+
+ // We don't send even end of stream on if we've previously
+ // returned something other than S_OK
+ // This is because in that case the pin which returned
+ // something other than S_OK should have either sent
+ // EndOfStream() or notified the filter graph
+
+ if (m_hr == S_OK) {
+ DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));
+ HRESULT hr = m_pPin->EndOfStream();
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));
+ }
+ }
+ }
+
+ // Data from a new source
+
+ if (pSample == RESET_PACKET) {
+ m_hr = S_OK;
+ SetEvent(m_evFlushComplete);
+ }
+
+ if (pSample == NEW_SEGMENT) {
+ m_pPin->NewSegment(ppacket->tStart, ppacket->tStop, ppacket->dRate);
+ delete ppacket;
+ }
+ }
+}
+
+// Send batched stuff anyway
+void COutputQueue::SendAnyway()
+{
+ if (!IsQueued()) {
+
+ // m_bSendAnyway is a private parameter checked in ReceiveMultiple
+
+ m_bSendAnyway = TRUE;
+ LONG nProcessed;
+ ReceiveMultiple(NULL, 0, &nProcessed);
+ m_bSendAnyway = FALSE;
+
+ } else {
+ CAutoLock lck(this);
+ QueueSample(SEND_PACKET);
+ NotifyThread();
+ }
+}
+
+void
+COutputQueue::NewSegment(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop,
+ double dRate)
+{
+ if (!IsQueued()) {
+ if (S_OK == m_hr) {
+ if (m_bBatchExact) {
+ SendAnyway();
+ }
+ m_pPin->NewSegment(tStart, tStop, dRate);
+ }
+ } else {
+ if (m_hr == S_OK) {
+ //
+ // we need to queue the new segment to appear in order in the
+ // data, but we need to pass parameters to it. Rather than
+ // take the hit of wrapping every single sample so we can tell
+ // special ones apart, we queue special pointers to indicate
+ // special packets, and we guarantee (by holding the
+ // critical section) that the packet immediately following a
+ // NEW_SEGMENT value is a NewSegmentPacket containing the
+ // parameters.
+ NewSegmentPacket * ppack = new NewSegmentPacket;
+ if (ppack == NULL) {
+ return;
+ }
+ ppack->tStart = tStart;
+ ppack->tStop = tStop;
+ ppack->dRate = dRate;
+
+ CAutoLock lck(this);
+ QueueSample(NEW_SEGMENT);
+ QueueSample( (IMediaSample*) ppack);
+ NotifyThread();
+ }
+ }
+}
+
+
+//
+// End of Stream is queued to output device
+//
+void COutputQueue::EOS()
+{
+ CAutoLock lck(this);
+ if (!IsQueued()) {
+ if (m_bBatchExact) {
+ SendAnyway();
+ }
+ if (m_hr == S_OK) {
+ DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));
+ m_bFlushed = FALSE;
+ HRESULT hr = m_pPin->EndOfStream();
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));
+ }
+ }
+ } else {
+ if (m_hr == S_OK) {
+ m_bFlushed = FALSE;
+ QueueSample(EOS_PACKET);
+ NotifyThread();
+ }
+ }
+}
+
+//
+// Flush all the samples in the queue
+//
+void COutputQueue::BeginFlush()
+{
+ if (IsQueued()) {
+ {
+ CAutoLock lck(this);
+
+ // block receives -- we assume this is done by the
+ // filter in which we are a component
+
+ // discard all queued data
+
+ m_bFlushing = TRUE;
+
+ // Make sure we discard all samples from now on
+
+ if (m_hr == S_OK) {
+ m_hr = S_FALSE;
+ }
+
+ // Optimize so we don't keep calling downstream all the time
+
+ if (m_bFlushed && m_bFlushingOpt) {
+ return;
+ }
+
+ // Make sure we really wait for the flush to complete
+ m_evFlushComplete.Reset();
+
+ NotifyThread();
+ }
+
+ // pass this downstream
+
+ m_pPin->BeginFlush();
+ } else {
+ // pass downstream first to avoid deadlocks
+ m_pPin->BeginFlush();
+ CAutoLock lck(this);
+ // discard all queued data
+
+ m_bFlushing = TRUE;
+
+ // Make sure we discard all samples from now on
+
+ if (m_hr == S_OK) {
+ m_hr = S_FALSE;
+ }
+ }
+
+}
+
+//
+// leave flush mode - pass this downstream
+void COutputQueue::EndFlush()
+{
+ {
+ CAutoLock lck(this);
+ ASSERT(m_bFlushing);
+ if (m_bFlushingOpt && m_bFlushed && IsQueued()) {
+ m_bFlushing = FALSE;
+ m_hr = S_OK;
+ return;
+ }
+ }
+
+ // sync with pushing thread -- done in BeginFlush
+ // ensure no more data to go downstream -- done in BeginFlush
+ //
+ // Because we are synching here there is no need to hold the critical
+ // section (in fact we'd deadlock if we did!)
+
+ if (IsQueued()) {
+ m_evFlushComplete.Wait();
+ } else {
+ FreeSamples();
+ }
+
+ // Be daring - the caller has guaranteed no samples will arrive
+ // before EndFlush() returns
+
+ m_bFlushing = FALSE;
+ m_bFlushed = TRUE;
+
+ // call EndFlush on downstream pins
+
+ m_pPin->EndFlush();
+
+ m_hr = S_OK;
+}
+
+// COutputQueue::QueueSample
+//
+// private method to Send a sample to the output queue
+// The critical section MUST be held when this is called
+
+void COutputQueue::QueueSample(IMediaSample *pSample)
+{
+ if (NULL == m_List->AddTail(pSample)) {
+ if (!IsSpecialSample(pSample)) {
+ pSample->Release();
+ }
+ }
+}
+
+//
+// COutputQueue::Receive()
+//
+// Send a single sample by the multiple sample route
+// (NOTE - this could be optimized if necessary)
+//
+// On return the sample will have been Release()'d
+//
+
+HRESULT COutputQueue::Receive(IMediaSample *pSample)
+{
+ LONG nProcessed;
+ return ReceiveMultiple(&pSample, 1, &nProcessed);
+}
+
+//
+// COutputQueue::ReceiveMultiple()
+//
+// Send a set of samples to the downstream pin
+//
+// ppSamples - array of samples
+// nSamples - how many
+// nSamplesProcessed - How many were processed
+//
+// On return all samples will have been Release()'d
+//
+
+HRESULT COutputQueue::ReceiveMultiple (
+ __in_ecount(nSamples) IMediaSample **ppSamples,
+ long nSamples,
+ __out long *nSamplesProcessed)
+{
+ if (nSamples < 0) {
+ return E_INVALIDARG;
+ }
+
+ CAutoLock lck(this);
+ // Either call directly or queue up the samples
+
+ if (!IsQueued()) {
+
+ // If we already had a bad return code then just return
+
+ if (S_OK != m_hr) {
+
+ // If we've never received anything since the last Flush()
+ // and the sticky return code is not S_OK we must be
+ // flushing
+ // ((!A || B) is equivalent to A implies B)
+ ASSERT(!m_bFlushed || m_bFlushing);
+
+ // We're supposed to Release() them anyway!
+ *nSamplesProcessed = 0;
+ for (int i = 0; i < nSamples; i++) {
+ DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (direct) : Discarding %d samples code 0x%8.8X"),
+ nSamples, m_hr));
+ ppSamples[i]->Release();
+ }
+
+ return m_hr;
+ }
+ //
+ // If we're flushing the sticky return code should be S_FALSE
+ //
+ ASSERT(!m_bFlushing);
+ m_bFlushed = FALSE;
+
+ ASSERT(m_nBatched < m_lBatchSize);
+ ASSERT(m_nBatched == 0 || m_bBatchExact);
+
+ // Loop processing the samples in batches
+
+ LONG iLost = 0;
+ long iDone = 0;
+ for (iDone = 0;
+ iDone < nSamples || (m_nBatched != 0 && m_bSendAnyway);
+ ) {
+
+//pragma message (REMIND("Implement threshold scheme"))
+ ASSERT(m_nBatched < m_lBatchSize);
+ if (iDone < nSamples) {
+ m_ppSamples[m_nBatched++] = ppSamples[iDone++];
+ }
+ if (m_nBatched == m_lBatchSize ||
+ nSamples == 0 && (m_bSendAnyway || !m_bBatchExact)) {
+ LONG nDone;
+ DbgLog((LOG_TRACE, 4, TEXT("Batching %d samples"),
+ m_nBatched));
+
+ if (m_hr == S_OK) {
+ m_hr = m_pInputPin->ReceiveMultiple(m_ppSamples,
+ m_nBatched,
+ &nDone);
+ } else {
+ nDone = 0;
+ }
+ iLost += m_nBatched - nDone;
+ for (LONG i = 0; i < m_nBatched; i++) {
+ m_ppSamples[i]->Release();
+ }
+ m_nBatched = 0;
+ }
+ }
+ *nSamplesProcessed = iDone - iLost;
+ if (*nSamplesProcessed < 0) {
+ *nSamplesProcessed = 0;
+ }
+ return m_hr;
+ } else {
+ /* We're sending to our thread */
+
+ if (m_hr != S_OK) {
+ *nSamplesProcessed = 0;
+ DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (queued) : Discarding %d samples code 0x%8.8X"),
+ nSamples, m_hr));
+ for (int i = 0; i < nSamples; i++) {
+ ppSamples[i]->Release();
+ }
+ return m_hr;
+ }
+ m_bFlushed = FALSE;
+ for (long i = 0; i < nSamples; i++) {
+ QueueSample(ppSamples[i]);
+ }
+ *nSamplesProcessed = nSamples;
+ if (!m_bBatchExact ||
+ m_nBatched + m_List->GetCount() >= m_lBatchSize) {
+ NotifyThread();
+ }
+ return S_OK;
+ }
+}
+
+// Get ready for new data - cancels sticky m_hr
+void COutputQueue::Reset()
+{
+ if (!IsQueued()) {
+ m_hr = S_OK;
+ } else {
+ {
+ CAutoLock lck(this);
+ QueueSample(RESET_PACKET);
+ NotifyThread();
+ }
+ m_evFlushComplete.Wait();
+ }
+}
+
+// Remove and Release() all queued and Batched samples
+void COutputQueue::FreeSamples()
+{
+ CAutoLock lck(this);
+ if (IsQueued()) {
+ while (TRUE) {
+ IMediaSample *pSample = m_List->RemoveHead();
+ // inform derived class we took something off the queue
+ if (m_hEventPop) {
+ //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
+ SetEvent(m_hEventPop);
+ }
+
+ if (pSample == NULL) {
+ break;
+ }
+ if (!IsSpecialSample(pSample)) {
+ pSample->Release();
+ } else {
+ if (pSample == NEW_SEGMENT) {
+ // Free NEW_SEGMENT packet
+ NewSegmentPacket *ppacket =
+ (NewSegmentPacket *) m_List->RemoveHead();
+ // inform derived class we took something off the queue
+ if (m_hEventPop) {
+ //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
+ SetEvent(m_hEventPop);
+ }
+
+ ASSERT(ppacket != NULL);
+ delete ppacket;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < m_nBatched; i++) {
+ m_ppSamples[i]->Release();
+ }
+ m_nBatched = 0;
+}
+
+// Notify the thread if there is something to do
+//
+// The critical section MUST be held when this is called
+void COutputQueue::NotifyThread()
+{
+ // Optimize - no need to signal if it's not waiting
+ ASSERT(IsQueued());
+ if (m_lWaiting) {
+ ReleaseSemaphore(m_hSem, m_lWaiting, NULL);
+ m_lWaiting = 0;
+ }
+}
+
+// See if there's any work to do
+// Returns
+// TRUE if there is nothing on the queue and nothing in the batch
+// and all data has been sent
+// FALSE otherwise
+//
+BOOL COutputQueue::IsIdle()
+{
+ CAutoLock lck(this);
+
+ // We're idle if
+ // there is no thread (!IsQueued()) OR
+ // the thread is waiting for more work (m_lWaiting != 0)
+ // AND
+ // there's nothing in the current batch (m_nBatched == 0)
+
+ if (IsQueued() && m_lWaiting == 0 || m_nBatched != 0) {
+ return FALSE;
+ } else {
+
+ // If we're idle it shouldn't be possible for there
+ // to be anything on the work queue
+
+ ASSERT(!IsQueued() || m_List->GetCount() == 0);
+ return TRUE;
+ }
+}
+
+
+void COutputQueue::SetPopEvent(HANDLE hEvent)
+{
+ m_hEventPop = hEvent;
+}
diff --git a/dshow_base/outputq.h b/dshow_base/outputq.h
index 3869fa7..db3d424 100644
--- a/dshow_base/outputq.h
+++ b/dshow_base/outputq.h
@@ -1,137 +1,137 @@
-//------------------------------------------------------------------------------
-// File: OutputQ.h
-//
-// Desc: DirectShow base classes - defines the COutputQueue class, which
-// makes a queue of samples and sends them to an output pin. The
-// class will optionally send the samples to the pin directly.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-typedef CGenericList CSampleList;
-
-class COutputQueue : public CCritSec
-{
-public:
- // Constructor
- COutputQueue(IPin *pInputPin, // Pin to send stuff to
- HRESULT *phr, // 'Return code'
- BOOL bAuto = TRUE, // Ask pin if blocks
- BOOL bQueue = TRUE, // Send through queue (ignored if
- // bAuto set)
- LONG lBatchSize = 1, // Batch
- BOOL bBatchExact = FALSE,// Batch exactly to BatchSize
- LONG lListSize = // Likely number in the list
- DEFAULTCACHE,
- DWORD dwPriority = // Priority of thread to create
- THREAD_PRIORITY_NORMAL,
- bool bFlushingOpt = false // flushing optimization
- );
- ~COutputQueue();
-
- // enter flush state - discard all data
- void BeginFlush(); // Begin flushing samples
-
- // re-enable receives (pass this downstream)
- void EndFlush(); // Complete flush of samples - downstream
- // pin guaranteed not to block at this stage
-
- void EOS(); // Call this on End of stream
-
- void SendAnyway(); // Send batched samples anyway (if bBatchExact set)
-
- void NewSegment(
- REFERENCE_TIME tStart,
- REFERENCE_TIME tStop,
- double dRate);
-
- HRESULT Receive(IMediaSample *pSample);
-
- // do something with these media samples
- HRESULT ReceiveMultiple (
- IMediaSample **pSamples,
- long nSamples,
- long *nSamplesProcessed);
-
- void Reset(); // Reset m_hr ready for more data
-
- // See if its idle or not
- BOOL IsIdle();
-
- // give the class an event to fire after everything removed from the queue
- void SetPopEvent(HANDLE hEvent);
-
-protected:
- static DWORD WINAPI InitialThreadProc(LPVOID pv);
- DWORD ThreadProc();
- BOOL IsQueued()
- {
- return m_List != NULL;
- }
-
- // The critical section MUST be held when this is called
- void QueueSample(IMediaSample *pSample);
-
- BOOL IsSpecialSample(IMediaSample *pSample)
- {
- return (DWORD_PTR)pSample > (DWORD_PTR)(LONG_PTR)(-16);
- }
-
- // Remove and Release() batched and queued samples
- void FreeSamples();
-
- // Notify the thread there is something to do
- void NotifyThread();
-
-
-protected:
- // Queue 'messages'
- #define SEND_PACKET ((IMediaSample *)(LONG_PTR)(-2)) // Send batch
- #define EOS_PACKET ((IMediaSample *)(LONG_PTR)(-3)) // End of stream
- #define RESET_PACKET ((IMediaSample *)(LONG_PTR)(-4)) // Reset m_hr
- #define NEW_SEGMENT ((IMediaSample *)(LONG_PTR)(-5)) // send NewSegment
-
- // new segment packet is always followed by one of these
- struct NewSegmentPacket {
- REFERENCE_TIME tStart;
- REFERENCE_TIME tStop;
- double dRate;
- };
-
- // Remember input stuff
- IPin * const m_pPin;
- IMemInputPin * m_pInputPin;
- BOOL const m_bBatchExact;
- LONG const m_lBatchSize;
-
- CSampleList * m_List;
- HANDLE m_hSem;
- CAMEvent m_evFlushComplete;
- HANDLE m_hThread;
- IMediaSample ** m_ppSamples;
- LONG m_nBatched;
-
- // Wait optimization
- LONG m_lWaiting;
- // Flush synchronization
- BOOL m_bFlushing;
-
- // flushing optimization. some downstream filters have trouble
- // with the queue's flushing optimization. other rely on it
- BOOL m_bFlushed;
- bool m_bFlushingOpt;
-
- // Terminate now
- BOOL m_bTerminate;
-
- // Send anyway flag for batching
- BOOL m_bSendAnyway;
-
- // Deferred 'return code'
- BOOL volatile m_hr;
-
- // an event that can be fired after every deliver
- HANDLE m_hEventPop;
-};
-
+//------------------------------------------------------------------------------
+// File: OutputQ.h
+//
+// Desc: DirectShow base classes - defines the COutputQueue class, which
+// makes a queue of samples and sends them to an output pin. The
+// class will optionally send the samples to the pin directly.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+typedef CGenericList CSampleList;
+
+class COutputQueue : public CCritSec
+{
+public:
+ // Constructor
+ COutputQueue(IPin *pInputPin, // Pin to send stuff to
+ __inout HRESULT *phr, // 'Return code'
+ BOOL bAuto = TRUE, // Ask pin if blocks
+ BOOL bQueue = TRUE, // Send through queue (ignored if
+ // bAuto set)
+ LONG lBatchSize = 1, // Batch
+ BOOL bBatchExact = FALSE,// Batch exactly to BatchSize
+ LONG lListSize = // Likely number in the list
+ DEFAULTCACHE,
+ DWORD dwPriority = // Priority of thread to create
+ THREAD_PRIORITY_NORMAL,
+ bool bFlushingOpt = false // flushing optimization
+ );
+ ~COutputQueue();
+
+ // enter flush state - discard all data
+ void BeginFlush(); // Begin flushing samples
+
+ // re-enable receives (pass this downstream)
+ void EndFlush(); // Complete flush of samples - downstream
+ // pin guaranteed not to block at this stage
+
+ void EOS(); // Call this on End of stream
+
+ void SendAnyway(); // Send batched samples anyway (if bBatchExact set)
+
+ void NewSegment(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop,
+ double dRate);
+
+ HRESULT Receive(IMediaSample *pSample);
+
+ // do something with these media samples
+ HRESULT ReceiveMultiple (
+ __in_ecount(nSamples) IMediaSample **pSamples,
+ long nSamples,
+ __out long *nSamplesProcessed);
+
+ void Reset(); // Reset m_hr ready for more data
+
+ // See if its idle or not
+ BOOL IsIdle();
+
+ // give the class an event to fire after everything removed from the queue
+ void SetPopEvent(HANDLE hEvent);
+
+protected:
+ static DWORD WINAPI InitialThreadProc(__in LPVOID pv);
+ DWORD ThreadProc();
+ BOOL IsQueued()
+ {
+ return m_List != NULL;
+ };
+
+ // The critical section MUST be held when this is called
+ void QueueSample(IMediaSample *pSample);
+
+ BOOL IsSpecialSample(IMediaSample *pSample)
+ {
+ return (DWORD_PTR)pSample > (DWORD_PTR)(LONG_PTR)(-16);
+ };
+
+ // Remove and Release() batched and queued samples
+ void FreeSamples();
+
+ // Notify the thread there is something to do
+ void NotifyThread();
+
+
+protected:
+ // Queue 'messages'
+ #define SEND_PACKET ((IMediaSample *)(LONG_PTR)(-2)) // Send batch
+ #define EOS_PACKET ((IMediaSample *)(LONG_PTR)(-3)) // End of stream
+ #define RESET_PACKET ((IMediaSample *)(LONG_PTR)(-4)) // Reset m_hr
+ #define NEW_SEGMENT ((IMediaSample *)(LONG_PTR)(-5)) // send NewSegment
+
+ // new segment packet is always followed by one of these
+ struct NewSegmentPacket {
+ REFERENCE_TIME tStart;
+ REFERENCE_TIME tStop;
+ double dRate;
+ };
+
+ // Remember input stuff
+ IPin * const m_pPin;
+ IMemInputPin * m_pInputPin;
+ BOOL const m_bBatchExact;
+ LONG const m_lBatchSize;
+
+ CSampleList * m_List;
+ HANDLE m_hSem;
+ CAMEvent m_evFlushComplete;
+ HANDLE m_hThread;
+ __field_ecount_opt(m_lBatchSize) IMediaSample ** m_ppSamples;
+ __range(0, m_lBatchSize) LONG m_nBatched;
+
+ // Wait optimization
+ LONG m_lWaiting;
+ // Flush synchronization
+ BOOL m_bFlushing;
+
+ // flushing optimization. some downstream filters have trouble
+ // with the queue's flushing optimization. other rely on it
+ BOOL m_bFlushed;
+ bool m_bFlushingOpt;
+
+ // Terminate now
+ BOOL m_bTerminate;
+
+ // Send anyway flag for batching
+ BOOL m_bSendAnyway;
+
+ // Deferred 'return code'
+ HRESULT volatile m_hr;
+
+ // an event that can be fired after every deliver
+ HANDLE m_hEventPop;
+};
+
diff --git a/dshow_base/perflog.cpp b/dshow_base/perflog.cpp
new file mode 100644
index 0000000..e642538
--- /dev/null
+++ b/dshow_base/perflog.cpp
@@ -0,0 +1,347 @@
+//------------------------------------------------------------------------------
+// File: perflog.cpp
+//
+// Desc: Macros for DirectShow performance logging.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+#pragma warning (disable:4201)
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "perflog.h"
+
+//
+// Local function prototypes.
+//
+
+ULONG
+WINAPI
+PerflogCallback (
+ WMIDPREQUESTCODE RequestCode,
+ __in PVOID Context,
+ __out ULONG* BufferSize,
+ __in PVOID Buffer
+ );
+
+//
+// Event tracing function pointers.
+// We have to do this to run on down-level platforms.
+//
+
+#ifdef UNICODE
+
+ULONG
+(__stdcall * _RegisterTraceGuids) (
+ __in IN WMIDPREQUEST RequestAddress,
+ __in IN PVOID RequestContext,
+ IN LPCGUID ControlGuid,
+ IN ULONG GuidCount,
+ __in IN PTRACE_GUID_REGISTRATION TraceGuidReg,
+ IN LPCWSTR MofImagePath,
+ IN LPCWSTR MofResourceName,
+ OUT PTRACEHANDLE RegistrationHandle
+ );
+
+#define REGISTERTRACEGUIDS_NAME "RegisterTraceGuidsW"
+
+#else
+
+ULONG
+(__stdcall * _RegisterTraceGuids) (
+ __in IN WMIDPREQUEST RequestAddress,
+ __in IN PVOID RequestContext,
+ IN LPCGUID ControlGuid,
+ IN ULONG GuidCount,
+ __in IN PTRACE_GUID_REGISTRATION TraceGuidReg,
+ IN LPCSTR MofImagePath,
+ IN LPCSTR MofResourceName,
+ __out OUT PTRACEHANDLE RegistrationHandle
+ );
+
+#define REGISTERTRACEGUIDS_NAME "RegisterTraceGuidsA"
+
+#endif
+
+ULONG
+(__stdcall * _UnregisterTraceGuids) (
+ TRACEHANDLE RegistrationHandle
+ );
+
+TRACEHANDLE
+(__stdcall * _GetTraceLoggerHandle) (
+ __in PVOID Buffer
+ );
+
+UCHAR
+(__stdcall * _GetTraceEnableLevel) (
+ TRACEHANDLE TraceHandle
+ );
+
+ULONG
+(__stdcall * _GetTraceEnableFlags) (
+ TRACEHANDLE TraceHandle
+ );
+
+ULONG
+(__stdcall * _TraceEvent) (
+ TRACEHANDLE TraceHandle,
+ __in PEVENT_TRACE_HEADER EventTrace
+ );
+
+HINSTANCE _Advapi32;
+
+//
+// Global variables.
+//
+
+BOOL EventTracingAvailable=FALSE;
+ULONG PerflogEnableFlags;
+UCHAR PerflogEnableLevel;
+ULONG PerflogModuleLevel = 0;
+void (*OnStateChanged)(void);
+TRACEHANDLE PerflogTraceHandle=NULL;
+TRACEHANDLE PerflogRegHandle;
+
+// The Win32 wsprintf() function writes a maximum of 1024 characters to it's output buffer.
+// See the documentation for wsprintf()'s lpOut parameter for more information.
+const INT iDEBUGINFO = 1024; // Used to format strings
+
+//
+// This routine initializes performance logging.
+// It should be called from DllMain().
+//
+
+
+VOID
+PerflogReadModuleLevel(
+ HINSTANCE hInstance
+ )
+{
+ LONG lReturn; // Create key return value
+ TCHAR szInfo[iDEBUGINFO]; // Constructs key names
+ TCHAR szFullName[iDEBUGINFO]; // Load the full path and module name
+ HKEY hModuleKey; // Module key handle
+ LPTSTR pName; // Searches from the end for a backslash
+ DWORD dwKeySize, dwKeyType, dwKeyValue;
+
+ DWORD dwSize = GetModuleFileName(
+ (hInstance ? hInstance : GetModuleHandle( NULL )),
+ szFullName,
+ iDEBUGINFO );
+
+ if (0 == dwSize || iDEBUGINFO == dwSize) {
+ return;
+ }
+
+ pName = _tcsrchr(szFullName,'\\');
+ if (pName == NULL) {
+ pName = szFullName;
+ } else {
+ pName++;
+ }
+
+ /* Construct the base key name */
+ (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("SOFTWARE\\Debug\\%s"),pName);
+
+ /* Open the key for this module */
+ lReturn =
+ RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE, // Handle of an open key
+ szInfo, // Address of subkey name
+ (DWORD) 0, // Reserved value
+ KEY_QUERY_VALUE, // Desired security access
+ &hModuleKey ); // Opened handle buffer
+
+ if (lReturn != ERROR_SUCCESS) {
+ return;
+ }
+
+ dwKeySize = sizeof(DWORD);
+ lReturn = RegQueryValueEx(
+ hModuleKey, // Handle to an open key
+ TEXT("PERFLOG"),
+ NULL, // Reserved field
+ &dwKeyType, // Returns the field type
+ (LPBYTE) &dwKeyValue, // Returns the field's value
+ &dwKeySize ); // Number of bytes transferred
+
+ if ((lReturn == ERROR_SUCCESS) && (dwKeyType == REG_DWORD))
+ {
+ PerflogModuleLevel = dwKeyValue;
+ }
+
+ RegCloseKey(hModuleKey);
+}
+
+BOOL PerflogInitIfEnabled(
+ IN HINSTANCE hInstance,
+ __in IN PPERFLOG_LOGGING_PARAMS LogParams
+ )
+{
+ PerflogReadModuleLevel( hInstance );
+ if (PerflogModuleLevel)
+ {
+ return PerflogInitialize( LogParams );
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL
+PerflogInitialize (
+ __in IN PPERFLOG_LOGGING_PARAMS LogParams
+ )
+{
+ ULONG status;
+
+ //
+ // If we're running on a recent-enough platform, this will get
+ // pointers to the event tracing routines.
+ //
+
+ _Advapi32 = GetModuleHandle (_T("ADVAPI32.DLL"));
+ if (_Advapi32 == NULL) {
+ return FALSE;
+ }
+
+ *((FARPROC*) &_RegisterTraceGuids) = GetProcAddress (_Advapi32, REGISTERTRACEGUIDS_NAME);
+ *((FARPROC*) &_UnregisterTraceGuids) = GetProcAddress (_Advapi32, "UnregisterTraceGuids");
+ *((FARPROC*) &_GetTraceLoggerHandle) = GetProcAddress (_Advapi32, "GetTraceLoggerHandle");
+ *((FARPROC*) &_GetTraceEnableLevel) = GetProcAddress (_Advapi32, "GetTraceEnableLevel");
+ *((FARPROC*) &_GetTraceEnableFlags) = GetProcAddress (_Advapi32, "GetTraceEnableFlags");
+ *((FARPROC*) &_TraceEvent) = GetProcAddress (_Advapi32, "TraceEvent");
+
+ if (_RegisterTraceGuids == NULL ||
+ _UnregisterTraceGuids == NULL ||
+ _GetTraceEnableLevel == NULL ||
+ _GetTraceEnableFlags == NULL ||
+ _TraceEvent == NULL) {
+
+ return FALSE;
+ }
+
+ EventTracingAvailable = TRUE;
+
+ OnStateChanged = LogParams->OnStateChanged;
+
+ //
+ // Register our GUIDs.
+ //
+
+ status = _RegisterTraceGuids (PerflogCallback,
+ LogParams,
+ &LogParams->ControlGuid,
+ LogParams->NumberOfTraceGuids,
+ LogParams->TraceGuids,
+ NULL,
+ NULL,
+ &PerflogRegHandle);
+
+ return (status == ERROR_SUCCESS);
+}
+
+//
+// This routine shuts down performance logging.
+//
+
+VOID
+PerflogShutdown (
+ VOID
+ )
+{
+ if (!EventTracingAvailable) {
+ return;
+ }
+
+ _UnregisterTraceGuids (PerflogRegHandle);
+ PerflogRegHandle = NULL;
+ PerflogTraceHandle = NULL;
+}
+
+//
+// Event tracing callback routine.
+// It's called when controllers call event tracing control functions.
+//
+
+ULONG
+WINAPI
+PerflogCallback (
+ WMIDPREQUESTCODE RequestCode,
+ __in PVOID Context,
+ __out ULONG* BufferSize,
+ __in PVOID Buffer
+ )
+{
+ ULONG status;
+
+ UNREFERENCED_PARAMETER (Context);
+
+ ASSERT (EventTracingAvailable);
+
+ status = ERROR_SUCCESS;
+
+ switch (RequestCode) {
+
+ case WMI_ENABLE_EVENTS:
+ PerflogTraceHandle = _GetTraceLoggerHandle (Buffer);
+ PerflogEnableFlags = _GetTraceEnableFlags (PerflogTraceHandle);
+ PerflogEnableLevel = _GetTraceEnableLevel (PerflogTraceHandle);
+ break;
+
+ case WMI_DISABLE_EVENTS:
+ PerflogTraceHandle = NULL;
+ PerflogEnableFlags = 0;
+ PerflogEnableLevel = 0;
+ break;
+
+ default:
+ status = ERROR_INVALID_PARAMETER;
+ }
+
+ if (OnStateChanged != NULL) {
+ OnStateChanged();
+ }
+
+ *BufferSize = 0;
+ return status;
+}
+
+//
+// Logging routine.
+//
+
+VOID
+PerflogTraceEvent (
+ __in PEVENT_TRACE_HEADER Event
+ )
+{
+ if (!EventTracingAvailable) {
+ return;
+ }
+
+ _TraceEvent (PerflogTraceHandle, Event);
+}
+
+VOID
+PerflogTraceEventLevel(
+ ULONG Level,
+ __in PEVENT_TRACE_HEADER Event
+ )
+{
+ if ((!EventTracingAvailable) || (Level <= PerflogModuleLevel)) {
+ return;
+ }
+
+ _TraceEvent (PerflogTraceHandle, Event);
+}
+
+
diff --git a/dshow_base/perflog.h b/dshow_base/perflog.h
new file mode 100644
index 0000000..503a130
--- /dev/null
+++ b/dshow_base/perflog.h
@@ -0,0 +1,56 @@
+//------------------------------------------------------------------------------
+// File: perflog.h
+//
+// Desc: Performance logging framework.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+typedef struct _PERFLOG_LOGGING_PARAMS {
+ GUID ControlGuid;
+ void (*OnStateChanged)(void);
+ ULONG NumberOfTraceGuids;
+ TRACE_GUID_REGISTRATION TraceGuids[ANYSIZE_ARRAY];
+} PERFLOG_LOGGING_PARAMS, *PPERFLOG_LOGGING_PARAMS;
+
+BOOL
+PerflogInitIfEnabled(
+ IN HINSTANCE hInstance,
+ __in PPERFLOG_LOGGING_PARAMS LogParams
+ );
+
+BOOL
+PerflogInitialize (
+ __in PPERFLOG_LOGGING_PARAMS LogParams
+ );
+
+VOID
+PerflogShutdown (
+ VOID
+ );
+
+VOID
+PerflogTraceEvent (
+ __in PEVENT_TRACE_HEADER Event
+ );
+
+extern ULONG PerflogEnableFlags;
+extern UCHAR PerflogEnableLevel;
+extern ULONG PerflogModuleLevel;
+extern TRACEHANDLE PerflogTraceHandle;
+extern TRACEHANDLE PerflogRegHandle;
+
+#define PerflogTracingEnabled() (PerflogTraceHandle != 0)
+
+#define PerflogEvent( _x_ ) PerflogTraceEventLevel _x_
+
+VOID
+PerflogTraceEventLevel(
+ ULONG Level,
+ __in PEVENT_TRACE_HEADER Event
+ );
+
+VOID
+PerflogTraceEvent (
+ __in PEVENT_TRACE_HEADER Event
+ );
diff --git a/dshow_base/perfstruct.h b/dshow_base/perfstruct.h
new file mode 100644
index 0000000..9c67b73
--- /dev/null
+++ b/dshow_base/perfstruct.h
@@ -0,0 +1,194 @@
+//------------------------------------------------------------------------------
+// File: PerfStruct.h
+//
+// Desc: Structures for DirectShow performance logging.
+//
+// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef _PERFSTRUCT_H_
+#define _PERFSTRUCT_H_
+
+#include
+#include
+
+// {28CF047A-2437-4b24-B653-B9446A419A69}
+DEFINE_GUID(GUID_DSHOW_CTL,
+0x28cf047a, 0x2437, 0x4b24, 0xb6, 0x53, 0xb9, 0x44, 0x6a, 0x41, 0x9a, 0x69);
+
+// {D0DA7AD6-AE80-4de5-AAFC-C126711E7593}
+DEFINE_GUID(GUID_VIDEOREND,
+0xd0da7ad6, 0xae80, 0x4de5, 0xaa, 0xfc, 0xc1, 0x26, 0x71, 0x1e, 0x75, 0x93);
+
+// {DC70AC3E-93E5-48db-88AB-E42064EC276A}
+DEFINE_GUID(GUID_DSOUNDGLITCH,
+0xdc70ac3e, 0x93e5, 0x48db, 0x88, 0xab, 0xe4, 0x20, 0x64, 0xec, 0x27, 0x6a);
+
+// {3d7e7d93-2fc8-4a07-a719-e0922ff2899}
+DEFINE_GUID(GUID_STREAMTRACE,
+0x3d7e7d93, 0x2fc8, 0x4a07, 0xa7, 0x19, 0xe0, 0x92, 0x2f, 0xf2, 0x89, 0x9e);
+
+// AZFIX: the following GUIDs aren't useful right now.
+
+// {3C33F7F5-EE54-493c-BA25-1656539C05AC}
+DEFINE_GUID(GUID_GETTIME,
+0x3c33f7f5, 0xee54, 0x493c, 0xba, 0x25, 0x16, 0x56, 0x53, 0x9c, 0x5, 0xac);
+
+// {CC44B44D-8169-4952-9E4A-A4E13295E492}
+DEFINE_GUID(GUID_AUDIOREND,
+0xcc44b44d, 0x8169, 0x4952, 0x9e, 0x4a, 0xa4, 0xe1, 0x32, 0x95, 0xe4, 0x92);
+
+// {775D19BF-4D8B-4de6-8DC9-66BAC7B310A2}
+DEFINE_GUID(GUID_FRAMEDROP,
+0x775d19bf, 0x4d8b, 0x4de6, 0x8d, 0xc9, 0x66, 0xba, 0xc7, 0xb3, 0x10, 0xa2);
+
+// {56D29065-EFBE-42dc-8C29-E325DC9C27D5}
+DEFINE_GUID(GUID_AUDIOBREAK,
+0x56d29065, 0xefbe, 0x42dc, 0x8c, 0x29, 0xe3, 0x25, 0xdc, 0x9c, 0x27, 0xd5);
+
+// {E1E6EA87-95A8-497e-BFBA-0295AEBCC707}
+DEFINE_GUID(GUID_AUDIORECV,
+0xe1e6ea87, 0x95a8, 0x497e, 0xbf, 0xba, 0x2, 0x95, 0xae, 0xbc, 0xc7, 0x7);
+
+// {10F7768A-B1E7-4242-AD90-A2D44683D9F0}
+DEFINE_GUID(GUID_AUDIOSLAVE,
+0x10f7768a, 0xb1e7, 0x4242, 0xad, 0x90, 0xa2, 0xd4, 0x46, 0x83, 0xd9, 0xf0);
+
+// {8983803D-691A-49bc-8FF6-962A39C0198F}
+DEFINE_GUID(GUID_AUDIOADDBREAK,
+0x8983803d, 0x691a, 0x49bc, 0x8f, 0xf6, 0x96, 0x2a, 0x39, 0xc0, 0x19, 0x8f);
+
+#define GLITCHTYPE_DSOUNDFIRSTGOOD 0
+#define GLITCHTYPE_DSOUNDFIRSTBAD 1
+
+typedef struct PERFINFO_DSHOW_AUDIOGLITCH {
+ ULONGLONG cycleCounter;
+ DWORD glitchType;
+ LONGLONG sampleTime;
+ LONGLONG previousTime;
+ ULONG_PTR instanceId;
+} PERFINFO_DSHOW_AUDIOGLITCH, *PPERFINFO_DSHOW_AUDIOGLITCH;
+
+typedef struct PERFINFO_WMI_AUDIOGLITCH {
+ EVENT_TRACE_HEADER header;
+ PERFINFO_DSHOW_AUDIOGLITCH data;
+} PERFINFO_WMI_AUDIO_GLITCH, *PPERFINFO_WMI_AUDIOGLITCH;
+
+typedef struct PERFINFO_DSHOW_GETTIME {
+ ULONGLONG cycleCounter;
+ ULONGLONG dshowClock;
+} PERFINFO_DSHOW_GETTIME, *PPERFINFO_DSHOW_GETTIME;
+
+typedef struct PERFINFO_WMI_GETTIME {
+ EVENT_TRACE_HEADER header;
+ PERFINFO_DSHOW_GETTIME data;
+} PERFINFO_WMI_GETTIME, *PPERFINFO_WMI_GETTIME;
+
+typedef struct PERFINFO_DSHOW_AVREND {
+ ULONGLONG cycleCounter;
+ ULONGLONG dshowClock;
+ ULONGLONG sampleTime;
+} PERFINFO_DSHOW_AVREND, *PPERFINFO_DSHOW_AVREND;
+
+typedef struct PERFINFO_WMI_AVREND {
+ EVENT_TRACE_HEADER header;
+ PERFINFO_DSHOW_AVREND data;
+} PERFINFO_WMI_AVREND, *PPERFINFO_WMI_AVREND;
+
+typedef struct PERFINFO_DSHOW_AUDIOBREAK {
+ ULONGLONG cycleCounter;
+ ULONGLONG dshowClock;
+ ULONGLONG sampleTime;
+ ULONGLONG sampleDuration;
+} PERFINFO_DSHOW_AUDIOBREAK, *PPERFINFO_DSHOW_AUDIOBREAK;
+
+typedef struct PERFINFO_WMI_AUDIOBREAK {
+ EVENT_TRACE_HEADER header;
+ PERFINFO_DSHOW_AUDIOBREAK data;
+} PERFINFO_WMI_AUDIOBREAK, *PPERFINFO_WMI_AUDIOBREAK;
+
+typedef struct PERFINFO_DSHOW_FRAMEDROP {
+ ULONGLONG cycleCounter;
+ ULONGLONG dshowClock;
+ ULONGLONG frameTime;
+} PERFINFO_DSHOW_FRAMEDROP, *PPERFINFO_DSHOW_FRAMEDROP;
+
+typedef struct PERFINFO_WMI_FRAMEDROP {
+ EVENT_TRACE_HEADER header;
+ PERFINFO_DSHOW_FRAMEDROP data;
+} PERFINFO_WMI_FRAMEDROP, *PPERFINFO_WMI_FRAMEDROP;
+
+#define PERFINFO_STREAMTRACE_MPEG2DEMUX_PTS_TRANSLATION 1
+#define PERFINFO_STREAMTRACE_MPEG2DEMUX_SAMPLE_RECEIVED 2
+#define PERFINFO_STREAMTRACE_VMR_BEGIN_ADVISE 3
+#define PERFINFO_STREAMTRACE_VMR_END_ADVISE 4
+#define PERFINFO_STREAMTRACE_VMR_RECEIVE 5
+#define PERFINFO_STREAMTRACE_VMR_BEGIN_DEINTERLACE 6
+#define PERFINFO_STREAMTRACE_VMR_END_DEINTERLACE 7
+#define PERFINFO_STREAMTRACE_VMR_BEGIN_DECODE 8
+#define PERFINFO_STREAMTRACE_VMR_END_DECODE 9
+#define PERFINFO_STREAMTRACE_VMR_DROPPED_FRAME 10
+#define PERFINFO_STREAMTRACE_ENCDEC_DTFILTERINPUT 11
+#define PERFINFO_STREAMTRACE_ENCDEC_DTFILTEROUTPUT 12
+#define PERFINFO_STREAMTRACE_ENCDEC_ETFILTERINPUT 13
+#define PERFINFO_STREAMTRACE_ENCDEC_ETFILTEROUTPUT 14
+#define PERFINFO_STREAMTRACE_ENCDEC_XDSCODECINPUT 15
+#define PERFINFO_STREAMTRACE_SBE_DVRANALYSISINPUT_RECEIVE 16
+#define PERFINFO_STREAMTRACE_SBE_DVRANALYSISINPUT_DELIVER 17
+#define PERFINFO_STREAMTRACE_SBE_DVRINPUTPIN_RECEIVE 18
+#define PERFINFO_STREAMTRACE_SBE_DVROUTPUTPIN_RECEIVE 19
+#define PERFINFO_STREAMTRACE_VMR_RENDER_TIME 20
+
+typedef struct _PERFINFO_DSHOW_STREAMTRACE {
+ ULONG id;
+ ULONG reserved;
+ ULONGLONG dshowClock;
+ ULONGLONG data[ 4 ];
+} PERFINFO_DSHOW_STREAMTRACE, *PPERFINFO_DSHOW_STREAMTRACE;
+
+typedef struct _PERFINFO_WMI_STREAMTRACE {
+ EVENT_TRACE_HEADER header;
+ PERFINFO_DSHOW_STREAMTRACE data;
+} PERFINFO_WMI_STREAMTRACE, *PPERFINFO_WMI_STREAMTRACE;
+
+
+typedef struct PERFINFO_DSHOW_AUDIORECV {
+ LONGLONG streamTime ;
+ LONGLONG sampleStart ;
+ LONGLONG sampleStop ;
+ LONGLONG hwduration ;
+ BOOL discontinuity ;
+} PERFINFO_DSHOW_AUDIORECV, *PPERFINFO_DSHOW_AUDIORECV;
+
+typedef struct PERFINFO_WMI_AUDIORECV {
+ EVENT_TRACE_HEADER header;
+ PERFINFO_DSHOW_AUDIORECV data;
+} PERFINFO_WMI_AUDIORECV, *PPERFINFO_WMI_AUDIORECV;
+
+typedef struct PERFINFO_DSHOW_AUDIOSLAVE {
+ LONGLONG masterClock ;
+ LONGLONG slaveClock ;
+ LONGLONG errorAccum ;
+ LONGLONG lastHighErrorSeen ;
+ LONGLONG lastLowErrorSeen ;
+} PERFINFO_DSHOW_AUDIOSLAVE, *PPERFINFO_DSHOW_AUDIOSLAVE;
+
+typedef struct PERFINFO_WMI_AUDIOSLAVE {
+ EVENT_TRACE_HEADER header;
+ PERFINFO_DSHOW_AUDIOSLAVE data;
+} PERFINFO_WMI_AUDIOSLAVE, *PPERFINFO_WMI_AUDIOSLAVE;
+
+typedef struct PERFINFO_DSHOW_AUDIOADDBREAK {
+ DWORD iterNextWrite ;
+ DWORD offsetNextWrite ;
+ DWORD iterWrite ;
+ DWORD offsetWrite ;
+} PERFINFO_DSHOW_AUDIOADDBREAK, *PPERFINFO_DSHOW_AUDIOADDBREAK;
+
+typedef struct PERFINFO_WMI_AUDIOADDBREAK {
+ EVENT_TRACE_HEADER header;
+ PERFINFO_DSHOW_AUDIOADDBREAK data;
+} PERFINFO_WMI_AUDIOADDBREAK, *PPERFINFO_WMI_AUDIOADDBREAK;
+
+#endif // _PREFSTRUCT_H_
diff --git a/dshow_base/pstream.cpp b/dshow_base/pstream.cpp
new file mode 100644
index 0000000..d20171f
--- /dev/null
+++ b/dshow_base/pstream.cpp
@@ -0,0 +1,197 @@
+//------------------------------------------------------------------------------
+// File: PStream.cpp
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#include
+
+#ifdef PERF
+#include
+#endif
+// #include "pstream.h" in streams.h
+
+//
+// Constructor
+//
+CPersistStream::CPersistStream(IUnknown *punk, __inout HRESULT *phr)
+ : mPS_fDirty(FALSE)
+{
+ mPS_dwFileVersion = GetSoftwareVersion();
+}
+
+
+//
+// Destructor
+//
+CPersistStream::~CPersistStream() {
+ // Nothing to do
+}
+
+#if 0
+SAMPLE CODE TO COPY - not active at the moment
+
+//
+// NonDelegatingQueryInterface
+//
+// This object supports IPersist & IPersistStream
+STDMETHODIMP CPersistStream::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ if (riid == IID_IPersist) {
+ return GetInterface((IPersist *) this, ppv); // ???
+ }
+ else if (riid == IID_IPersistStream) {
+ return GetInterface((IPersistStream *) this, ppv);
+ }
+ else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+#endif
+
+
+//
+// WriteToStream
+//
+// Writes to the stream (default action is to write nothing)
+HRESULT CPersistStream::WriteToStream(IStream *pStream)
+{
+ // You can override this to do things like
+ // hr = pStream->Write(MyStructure, sizeof(MyStructure), NULL);
+
+ return NOERROR;
+}
+
+
+
+HRESULT CPersistStream::ReadFromStream(IStream * pStream)
+{
+ // You can override this to do things like
+ // hr = pStream->Read(MyStructure, sizeof(MyStructure), NULL);
+
+ return NOERROR;
+}
+
+
+//
+// Load
+//
+// Load all the data from the given stream
+STDMETHODIMP CPersistStream::Load(LPSTREAM pStm)
+{
+ HRESULT hr;
+ // Load the version number then the data
+ mPS_dwFileVersion = ReadInt(pStm, hr);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return ReadFromStream(pStm);
+} // Load
+
+
+
+//
+// Save
+//
+// Save the contents of this Stream.
+STDMETHODIMP CPersistStream::Save(LPSTREAM pStm, BOOL fClearDirty)
+{
+
+ HRESULT hr = WriteInt(pStm, GetSoftwareVersion());
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = WriteToStream(pStm);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ mPS_fDirty = !fClearDirty;
+
+ return hr;
+} // Save
+
+
+// WriteInt
+//
+// Writes an integer to an IStream as 11 UNICODE characters followed by one space.
+// You could use this for shorts or unsigneds or anything (up to 32 bits)
+// where the value isn't actually truncated by squeezing it into 32 bits.
+// Values such as (unsigned) 0x80000000 would come out as -2147483648
+// but would then load as 0x80000000 through ReadInt. Cast as you please.
+
+STDAPI WriteInt(IStream *pIStream, int n)
+{
+ WCHAR Buff[13]; // Allows for trailing null that we don't write
+ (void)StringCchPrintfW(Buff, NUMELMS(Buff),L"%011d ",n);
+ return pIStream->Write(&(Buff[0]), 12*sizeof(WCHAR), NULL);
+} // WriteInt
+
+
+// ReadInt
+//
+// Reads an integer from an IStream.
+// Read as 4 bytes. You could use this for shorts or unsigneds or anything
+// where the value isn't actually truncated by squeezing it into 32 bits
+// Striped down subset of what sscanf can do (without dragging in the C runtime)
+
+STDAPI_(int) ReadInt(IStream *pIStream, __out HRESULT &hr)
+{
+
+ int Sign = 1;
+ unsigned int n = 0; // result wil be n*Sign
+ WCHAR wch;
+
+ hr = pIStream->Read( &wch, sizeof(wch), NULL);
+ if (FAILED(hr)) {
+ return 0;
+ }
+
+ if (wch==L'-'){
+ Sign = -1;
+ hr = pIStream->Read( &wch, sizeof(wch), NULL);
+ if (FAILED(hr)) {
+ return 0;
+ }
+ }
+
+ for( ; ; ) {
+ if (wch>=L'0' && wch<=L'9') {
+ n = 10*n+(int)(wch-L'0');
+ } else if ( wch == L' '
+ || wch == L'\t'
+ || wch == L'\r'
+ || wch == L'\n'
+ || wch == L'\0'
+ ) {
+ break;
+ } else {
+ hr = VFW_E_INVALID_FILE_FORMAT;
+ return 0;
+ }
+
+ hr = pIStream->Read( &wch, sizeof(wch), NULL);
+ if (FAILED(hr)) {
+ return 0;
+ }
+ }
+
+ if (n==0x80000000 && Sign==-1) {
+ // This is the negative number that has no positive version!
+ return (int)n;
+ }
+ else return (int)n * Sign;
+} // ReadInt
+
+
+// The microsoft C/C++ compile generates level 4 warnings to the effect that
+// a particular inline function (from some base class) was not needed.
+// This line gets rid of hundreds of such unwanted messages and makes
+// -W4 compilation feasible:
+#pragma warning(disable: 4514)
diff --git a/dshow_base/pstream.h b/dshow_base/pstream.h
index 0611a4e..04b6af6 100644
--- a/dshow_base/pstream.h
+++ b/dshow_base/pstream.h
@@ -1,114 +1,114 @@
-//------------------------------------------------------------------------------
-// File: PStream.h
-//
-// Desc: DirectShow base classes - defines a class for persistent properties
-// of filters.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __PSTREAM__
-#define __PSTREAM__
-
-// Base class for persistent properties of filters
-// (i.e. filter properties in saved graphs)
-
-// The simplest way to use this is:
-// 1. Arrange for your filter to inherit this class
-// 2. Implement in your class WriteToStream and ReadFromStream
-// These will override the "do nothing" functions here.
-// 3. Change your NonDelegatingQueryInterface to handle IPersistStream
-// 4. Implement SizeMax to return the number of bytes of data you save.
-// If you save UNICODE data, don't forget a char is 2 bytes.
-// 5. Whenever your data changes, call SetDirty()
-//
-// At some point you may decide to alter, or extend the format of your data.
-// At that point you will wish that you had a version number in all the old
-// saved graphs, so that you can tell, when you read them, whether they
-// represent the old or new form. To assist you in this, this class
-// writes and reads a version number.
-// When it writes, it calls GetSoftwareVersion() to enquire what version
-// of the software we have at the moment. (In effect this is a version number
-// of the data layout in the file). It writes this as the first thing in the data.
-// If you want to change the version, implement (override) GetSoftwareVersion().
-// It reads this from the file into mPS_dwFileVersion before calling ReadFromStream,
-// so in ReadFromStream you can check mPS_dwFileVersion to see if you are reading
-// an old version file.
-// Normally you should accept files whose version is no newer than the software
-// version that's reading them.
-
-
-// CPersistStream
-//
-// Implements IPersistStream.
-// See 'OLE Programmers Reference (Vol 1):Structured Storage Overview' for
-// more implementation information.
-class CPersistStream : public IPersistStream {
- private:
-
- // Internal state:
-
- protected:
- DWORD mPS_dwFileVersion; // version number of file (being read)
- BOOL mPS_fDirty;
-
- public:
-
- // IPersistStream methods
-
- STDMETHODIMP IsDirty()
- {return (mPS_fDirty ? S_OK : S_FALSE);} // note FALSE means clean
- STDMETHODIMP Load(LPSTREAM pStm);
- STDMETHODIMP Save(LPSTREAM pStm, BOOL fClearDirty);
- STDMETHODIMP GetSizeMax(ULARGE_INTEGER * pcbSize)
- // Allow 24 bytes for version.
- { pcbSize->QuadPart = 12*sizeof(WCHAR)+SizeMax(); return NOERROR; }
-
- // implementation
-
- CPersistStream(IUnknown *punk, HRESULT *phr);
- ~CPersistStream();
-
- HRESULT SetDirty(BOOL fDirty)
- { mPS_fDirty = fDirty; return NOERROR;}
-
-
- // override to reveal IPersist & IPersistStream
- // STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- // --- IPersist ---
-
- // You must override this to provide your own class id
- STDMETHODIMP GetClassID(CLSID *pClsid) PURE;
-
- // overrideable if you want
- // file version number. Override it if you ever change format
- virtual DWORD GetSoftwareVersion(void) { return 0; }
-
-
- //=========================================================================
- // OVERRIDE THESE to read and write your data
- // OVERRIDE THESE to read and write your data
- // OVERRIDE THESE to read and write your data
-
- virtual int SizeMax() {return 0;}
- virtual HRESULT WriteToStream(IStream *pStream);
- virtual HRESULT ReadFromStream(IStream *pStream);
- //=========================================================================
-
- private:
-
-};
-
-
-// --- Useful helpers ---
-
-
-// Writes an int to an IStream as UNICODE.
-STDAPI WriteInt(IStream *pIStream, int n);
-
-// inverse of WriteInt
-STDAPI_(int) ReadInt(IStream *pIStream, HRESULT &hr);
-
-#endif // __PSTREAM__
+//------------------------------------------------------------------------------
+// File: PStream.h
+//
+// Desc: DirectShow base classes - defines a class for persistent properties
+// of filters.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __PSTREAM__
+#define __PSTREAM__
+
+// Base class for persistent properties of filters
+// (i.e. filter properties in saved graphs)
+
+// The simplest way to use this is:
+// 1. Arrange for your filter to inherit this class
+// 2. Implement in your class WriteToStream and ReadFromStream
+// These will override the "do nothing" functions here.
+// 3. Change your NonDelegatingQueryInterface to handle IPersistStream
+// 4. Implement SizeMax to return the number of bytes of data you save.
+// If you save UNICODE data, don't forget a char is 2 bytes.
+// 5. Whenever your data changes, call SetDirty()
+//
+// At some point you may decide to alter, or extend the format of your data.
+// At that point you will wish that you had a version number in all the old
+// saved graphs, so that you can tell, when you read them, whether they
+// represent the old or new form. To assist you in this, this class
+// writes and reads a version number.
+// When it writes, it calls GetSoftwareVersion() to enquire what version
+// of the software we have at the moment. (In effect this is a version number
+// of the data layout in the file). It writes this as the first thing in the data.
+// If you want to change the version, implement (override) GetSoftwareVersion().
+// It reads this from the file into mPS_dwFileVersion before calling ReadFromStream,
+// so in ReadFromStream you can check mPS_dwFileVersion to see if you are reading
+// an old version file.
+// Normally you should accept files whose version is no newer than the software
+// version that's reading them.
+
+
+// CPersistStream
+//
+// Implements IPersistStream.
+// See 'OLE Programmers Reference (Vol 1):Structured Storage Overview' for
+// more implementation information.
+class CPersistStream : public IPersistStream {
+ private:
+
+ // Internal state:
+
+ protected:
+ DWORD mPS_dwFileVersion; // version number of file (being read)
+ BOOL mPS_fDirty;
+
+ public:
+
+ // IPersistStream methods
+
+ STDMETHODIMP IsDirty()
+ {return (mPS_fDirty ? S_OK : S_FALSE);} // note FALSE means clean
+ STDMETHODIMP Load(LPSTREAM pStm);
+ STDMETHODIMP Save(LPSTREAM pStm, BOOL fClearDirty);
+ STDMETHODIMP GetSizeMax(__out ULARGE_INTEGER * pcbSize)
+ // Allow 24 bytes for version.
+ { pcbSize->QuadPart = 12*sizeof(WCHAR)+SizeMax(); return NOERROR; }
+
+ // implementation
+
+ CPersistStream(IUnknown *punk, __inout HRESULT *phr);
+ ~CPersistStream();
+
+ HRESULT SetDirty(BOOL fDirty)
+ { mPS_fDirty = fDirty; return NOERROR;}
+
+
+ // override to reveal IPersist & IPersistStream
+ // STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
+
+ // --- IPersist ---
+
+ // You must override this to provide your own class id
+ STDMETHODIMP GetClassID(__out CLSID *pClsid) PURE;
+
+ // overrideable if you want
+ // file version number. Override it if you ever change format
+ virtual DWORD GetSoftwareVersion(void) { return 0; }
+
+
+ //=========================================================================
+ // OVERRIDE THESE to read and write your data
+ // OVERRIDE THESE to read and write your data
+ // OVERRIDE THESE to read and write your data
+
+ virtual int SizeMax() {return 0;}
+ virtual HRESULT WriteToStream(IStream *pStream);
+ virtual HRESULT ReadFromStream(IStream *pStream);
+ //=========================================================================
+
+ private:
+
+};
+
+
+// --- Useful helpers ---
+
+
+// Writes an int to an IStream as UNICODE.
+STDAPI WriteInt(IStream *pIStream, int n);
+
+// inverse of WriteInt
+STDAPI_(int) ReadInt(IStream *pIStream, __out HRESULT &hr);
+
+#endif // __PSTREAM__
diff --git a/dshow_base/pullpin.cpp b/dshow_base/pullpin.cpp
new file mode 100644
index 0000000..a197ba5
--- /dev/null
+++ b/dshow_base/pullpin.cpp
@@ -0,0 +1,588 @@
+//------------------------------------------------------------------------------
+// File: PullPin.cpp
+//
+// Desc: DirectShow base classes - implements CPullPin class that pulls data
+// from IAsyncReader.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#include "pullpin.h"
+
+#ifdef DXMPERF
+#include "dxmperf.h"
+#endif // DXMPERF
+
+
+CPullPin::CPullPin()
+ : m_pReader(NULL),
+ m_pAlloc(NULL),
+ m_State(TM_Exit)
+{
+#ifdef DXMPERF
+ PERFLOG_CTOR( L"CPullPin", this );
+#endif // DXMPERF
+
+}
+
+CPullPin::~CPullPin()
+{
+ Disconnect();
+
+#ifdef DXMPERF
+ PERFLOG_DTOR( L"CPullPin", this );
+#endif // DXMPERF
+
+}
+
+// returns S_OK if successfully connected to an IAsyncReader interface
+// from this object
+// Optional allocator should be proposed as a preferred allocator if
+// necessary
+HRESULT
+CPullPin::Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync)
+{
+ CAutoLock lock(&m_AccessLock);
+
+ if (m_pReader) {
+ return VFW_E_ALREADY_CONNECTED;
+ }
+
+ HRESULT hr = pUnk->QueryInterface(IID_IAsyncReader, (void**)&m_pReader);
+ if (FAILED(hr)) {
+
+#ifdef DXMPERF
+ {
+ AM_MEDIA_TYPE * pmt = NULL;
+ PERFLOG_CONNECT( this, pUnk, hr, pmt );
+ }
+#endif // DXMPERF
+
+ return(hr);
+ }
+
+ hr = DecideAllocator(pAlloc, NULL);
+ if (FAILED(hr)) {
+ Disconnect();
+
+#ifdef DXMPERF
+ {
+ AM_MEDIA_TYPE * pmt = NULL;
+ PERFLOG_CONNECT( this, pUnk, hr, pmt );
+ }
+#endif // DXMPERF
+
+ return hr;
+ }
+
+ LONGLONG llTotal, llAvail;
+ hr = m_pReader->Length(&llTotal, &llAvail);
+ if (FAILED(hr)) {
+ Disconnect();
+
+#ifdef DXMPERF
+ {
+ AM_MEDIA_TYPE * pmt = NULL;
+ PERFLOG_CONNECT( this, pUnk, hr, pmt );
+ }
+#endif
+
+ return hr;
+ }
+
+ // convert from file position to reference time
+ m_tDuration = llTotal * UNITS;
+ m_tStop = m_tDuration;
+ m_tStart = 0;
+
+ m_bSync = bSync;
+
+#ifdef DXMPERF
+ {
+ AM_MEDIA_TYPE * pmt = NULL;
+ PERFLOG_CONNECT( this, pUnk, S_OK, pmt );
+ }
+#endif // DXMPERF
+
+
+ return S_OK;
+}
+
+// disconnect any connection made in Connect
+HRESULT
+CPullPin::Disconnect()
+{
+ CAutoLock lock(&m_AccessLock);
+
+ StopThread();
+
+
+#ifdef DXMPERF
+ PERFLOG_DISCONNECT( this, m_pReader, S_OK );
+#endif // DXMPERF
+
+
+ if (m_pReader) {
+ m_pReader->Release();
+ m_pReader = NULL;
+ }
+
+ if (m_pAlloc) {
+ m_pAlloc->Release();
+ m_pAlloc = NULL;
+ }
+
+ return S_OK;
+}
+
+// agree an allocator using RequestAllocator - optional
+// props param specifies your requirements (non-zero fields).
+// returns an error code if fail to match requirements.
+// optional IMemAllocator interface is offered as a preferred allocator
+// but no error occurs if it can't be met.
+HRESULT
+CPullPin::DecideAllocator(
+ IMemAllocator * pAlloc,
+ __inout_opt ALLOCATOR_PROPERTIES * pProps)
+{
+ ALLOCATOR_PROPERTIES *pRequest;
+ ALLOCATOR_PROPERTIES Request;
+ if (pProps == NULL) {
+ Request.cBuffers = 3;
+ Request.cbBuffer = 64*1024;
+ Request.cbAlign = 0;
+ Request.cbPrefix = 0;
+ pRequest = &Request;
+ } else {
+ pRequest = pProps;
+ }
+ HRESULT hr = m_pReader->RequestAllocator(
+ pAlloc,
+ pRequest,
+ &m_pAlloc);
+ return hr;
+}
+
+// start pulling data
+HRESULT
+CPullPin::Active(void)
+{
+ ASSERT(!ThreadExists());
+ return StartThread();
+}
+
+// stop pulling data
+HRESULT
+CPullPin::Inactive(void)
+{
+ StopThread();
+
+ return S_OK;
+}
+
+HRESULT
+CPullPin::Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop)
+{
+ CAutoLock lock(&m_AccessLock);
+
+ ThreadMsg AtStart = m_State;
+
+ if (AtStart == TM_Start) {
+ BeginFlush();
+ PauseThread();
+ EndFlush();
+ }
+
+ m_tStart = tStart;
+ m_tStop = tStop;
+
+ HRESULT hr = S_OK;
+ if (AtStart == TM_Start) {
+ hr = StartThread();
+ }
+
+ return hr;
+}
+
+HRESULT
+CPullPin::Duration(__out REFERENCE_TIME* ptDuration)
+{
+ *ptDuration = m_tDuration;
+ return S_OK;
+}
+
+
+HRESULT
+CPullPin::StartThread()
+{
+ CAutoLock lock(&m_AccessLock);
+
+ if (!m_pAlloc || !m_pReader) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr;
+ if (!ThreadExists()) {
+
+ // commit allocator
+ hr = m_pAlloc->Commit();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // start thread
+ if (!Create()) {
+ return E_FAIL;
+ }
+ }
+
+ m_State = TM_Start;
+ hr = (HRESULT) CallWorker(m_State);
+ return hr;
+}
+
+HRESULT
+CPullPin::PauseThread()
+{
+ CAutoLock lock(&m_AccessLock);
+
+ if (!ThreadExists()) {
+ return E_UNEXPECTED;
+ }
+
+ // need to flush to ensure the thread is not blocked
+ // in WaitForNext
+ HRESULT hr = m_pReader->BeginFlush();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ m_State = TM_Pause;
+ hr = CallWorker(TM_Pause);
+
+ m_pReader->EndFlush();
+ return hr;
+}
+
+HRESULT
+CPullPin::StopThread()
+{
+ CAutoLock lock(&m_AccessLock);
+
+ if (!ThreadExists()) {
+ return S_FALSE;
+ }
+
+ // need to flush to ensure the thread is not blocked
+ // in WaitForNext
+ HRESULT hr = m_pReader->BeginFlush();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ m_State = TM_Exit;
+ hr = CallWorker(TM_Exit);
+
+ m_pReader->EndFlush();
+
+ // wait for thread to completely exit
+ Close();
+
+ // decommit allocator
+ if (m_pAlloc) {
+ m_pAlloc->Decommit();
+ }
+
+ return S_OK;
+}
+
+
+DWORD
+CPullPin::ThreadProc(void)
+{
+ while(1) {
+ DWORD cmd = GetRequest();
+ switch(cmd) {
+ case TM_Exit:
+ Reply(S_OK);
+ return 0;
+
+ case TM_Pause:
+ // we are paused already
+ Reply(S_OK);
+ break;
+
+ case TM_Start:
+ Reply(S_OK);
+ Process();
+ break;
+ }
+
+ // at this point, there should be no outstanding requests on the
+ // upstream filter.
+ // We should force begin/endflush to ensure that this is true.
+ // !!!Note that we may currently be inside a BeginFlush/EndFlush pair
+ // on another thread, but the premature EndFlush will do no harm now
+ // that we are idle.
+ m_pReader->BeginFlush();
+ CleanupCancelled();
+ m_pReader->EndFlush();
+ }
+}
+
+HRESULT
+CPullPin::QueueSample(
+ __inout REFERENCE_TIME& tCurrent,
+ REFERENCE_TIME tAlignStop,
+ BOOL bDiscontinuity
+ )
+{
+ IMediaSample* pSample;
+
+ HRESULT hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS);
+ if (tStopThis > tAlignStop) {
+ tStopThis = tAlignStop;
+ }
+ pSample->SetTime(&tCurrent, &tStopThis);
+ tCurrent = tStopThis;
+
+ pSample->SetDiscontinuity(bDiscontinuity);
+
+ hr = m_pReader->Request(
+ pSample,
+ 0);
+ if (FAILED(hr)) {
+ pSample->Release();
+
+ CleanupCancelled();
+ OnError(hr);
+ }
+ return hr;
+}
+
+HRESULT
+CPullPin::CollectAndDeliver(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop)
+{
+ IMediaSample* pSample = NULL; // better be sure pSample is set
+ DWORD_PTR dwUnused;
+ HRESULT hr = m_pReader->WaitForNext(
+ INFINITE,
+ &pSample,
+ &dwUnused);
+ if (FAILED(hr)) {
+ if (pSample) {
+ pSample->Release();
+ }
+ } else {
+ hr = DeliverSample(pSample, tStart, tStop);
+ }
+ if (FAILED(hr)) {
+ CleanupCancelled();
+ OnError(hr);
+ }
+ return hr;
+
+}
+
+HRESULT
+CPullPin::DeliverSample(
+ IMediaSample* pSample,
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop
+ )
+{
+ // fix up sample if past actual stop (for sector alignment)
+ REFERENCE_TIME t1, t2;
+ if (S_OK == pSample->GetTime(&t1, &t2)) {
+ if (t2 > tStop) {
+ t2 = tStop;
+ }
+
+ // adjust times to be relative to (aligned) start time
+ t1 -= tStart;
+ t2 -= tStart;
+ HRESULT hr = pSample->SetTime(&t1, &t2);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+
+#ifdef DXMPERF
+ {
+ AM_MEDIA_TYPE * pmt = NULL;
+ pSample->GetMediaType( &pmt );
+ PERFLOG_RECEIVE( L"CPullPin", m_pReader, this, pSample, pmt );
+ }
+#endif
+
+ HRESULT hr = Receive(pSample);
+ pSample->Release();
+ return hr;
+}
+
+void
+CPullPin::Process(void)
+{
+ // is there anything to do?
+ if (m_tStop <= m_tStart) {
+ EndOfStream();
+ return;
+ }
+
+ BOOL bDiscontinuity = TRUE;
+
+ // if there is more than one sample at the allocator,
+ // then try to queue 2 at once in order to overlap.
+ // -- get buffer count and required alignment
+ ALLOCATOR_PROPERTIES Actual;
+ HRESULT hr = m_pAlloc->GetProperties(&Actual);
+
+ // align the start position downwards
+ REFERENCE_TIME tStart = AlignDown(m_tStart / UNITS, Actual.cbAlign) * UNITS;
+ REFERENCE_TIME tCurrent = tStart;
+
+ REFERENCE_TIME tStop = m_tStop;
+ if (tStop > m_tDuration) {
+ tStop = m_tDuration;
+ }
+
+ // align the stop position - may be past stop, but that
+ // doesn't matter
+ REFERENCE_TIME tAlignStop = AlignUp(tStop / UNITS, Actual.cbAlign) * UNITS;
+
+
+ DWORD dwRequest;
+
+ if (!m_bSync) {
+
+ // Break out of the loop either if we get to the end or we're asked
+ // to do something else
+ while (tCurrent < tAlignStop) {
+
+ // Break out without calling EndOfStream if we're asked to
+ // do something different
+ if (CheckRequest(&dwRequest)) {
+ return;
+ }
+
+ // queue a first sample
+ if (Actual.cBuffers > 1) {
+
+ hr = QueueSample(tCurrent, tAlignStop, TRUE);
+ bDiscontinuity = FALSE;
+
+ if (FAILED(hr)) {
+ return;
+ }
+ }
+
+
+
+ // loop queueing second and waiting for first..
+ while (tCurrent < tAlignStop) {
+
+ hr = QueueSample(tCurrent, tAlignStop, bDiscontinuity);
+ bDiscontinuity = FALSE;
+
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = CollectAndDeliver(tStart, tStop);
+ if (S_OK != hr) {
+
+ // stop if error, or if downstream filter said
+ // to stop.
+ return;
+ }
+ }
+
+ if (Actual.cBuffers > 1) {
+ hr = CollectAndDeliver(tStart, tStop);
+ if (FAILED(hr)) {
+ return;
+ }
+ }
+ }
+ } else {
+
+ // sync version of above loop
+ while (tCurrent < tAlignStop) {
+
+ // Break out without calling EndOfStream if we're asked to
+ // do something different
+ if (CheckRequest(&dwRequest)) {
+ return;
+ }
+
+ IMediaSample* pSample;
+
+ hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0);
+ if (FAILED(hr)) {
+ OnError(hr);
+ return;
+ }
+
+ LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS);
+ if (tStopThis > tAlignStop) {
+ tStopThis = tAlignStop;
+ }
+ pSample->SetTime(&tCurrent, &tStopThis);
+ tCurrent = tStopThis;
+
+ if (bDiscontinuity) {
+ pSample->SetDiscontinuity(TRUE);
+ bDiscontinuity = FALSE;
+ }
+
+ hr = m_pReader->SyncReadAligned(pSample);
+
+ if (FAILED(hr)) {
+ pSample->Release();
+ OnError(hr);
+ return;
+ }
+
+ hr = DeliverSample(pSample, tStart, tStop);
+ if (hr != S_OK) {
+ if (FAILED(hr)) {
+ OnError(hr);
+ }
+ return;
+ }
+ }
+ }
+
+ EndOfStream();
+}
+
+// after a flush, cancelled i/o will be waiting for collection
+// and release
+void
+CPullPin::CleanupCancelled(void)
+{
+ while (1) {
+ IMediaSample * pSample;
+ DWORD_PTR dwUnused;
+
+ HRESULT hr = m_pReader->WaitForNext(
+ 0, // no wait
+ &pSample,
+ &dwUnused);
+ if(pSample) {
+ pSample->Release();
+ } else {
+ // no more samples
+ return;
+ }
+ }
+}
diff --git a/dshow_base/pullpin.h b/dshow_base/pullpin.h
index 6546701..03ad40e 100644
--- a/dshow_base/pullpin.h
+++ b/dshow_base/pullpin.h
@@ -1,152 +1,152 @@
-//------------------------------------------------------------------------------
-// File: PullPin.h
-//
-// Desc: DirectShow base classes - defines CPullPin class.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __PULLPIN_H__
-#define __PULLPIN_H__
-
-//
-// CPullPin
-//
-// object supporting pulling data from an IAsyncReader interface.
-// Given a start/stop position, calls a pure Receive method with each
-// IMediaSample received.
-//
-// This is essentially for use in a MemInputPin when it finds itself
-// connected to an IAsyncReader pin instead of a pushing pin.
-//
-
-class CPullPin : public CAMThread
-{
- IAsyncReader* m_pReader;
- REFERENCE_TIME m_tStart;
- REFERENCE_TIME m_tStop;
- REFERENCE_TIME m_tDuration;
- BOOL m_bSync;
-
- enum ThreadMsg {
- TM_Pause, // stop pulling and wait for next message
- TM_Start, // start pulling
- TM_Exit, // stop and exit
- };
-
- ThreadMsg m_State;
-
- // override pure thread proc from CAMThread
- DWORD ThreadProc(void);
-
- // running pull method (check m_bSync)
- void Process(void);
-
- // clean up any cancelled i/o after a flush
- void CleanupCancelled(void);
-
- // suspend thread from pulling, eg during seek
- HRESULT PauseThread();
-
- // start thread pulling - create thread if necy
- HRESULT StartThread();
-
- // stop and close thread
- HRESULT StopThread();
-
- // called from ProcessAsync to queue and collect requests
- HRESULT QueueSample(
- REFERENCE_TIME& tCurrent,
- REFERENCE_TIME tAlignStop,
- BOOL bDiscontinuity);
-
- HRESULT CollectAndDeliver(
- REFERENCE_TIME tStart,
- REFERENCE_TIME tStop);
-
- HRESULT DeliverSample(
- IMediaSample* pSample,
- REFERENCE_TIME tStart,
- REFERENCE_TIME tStop);
-
-protected:
- IMemAllocator * m_pAlloc;
-
-public:
- CPullPin();
- virtual ~CPullPin();
-
- // returns S_OK if successfully connected to an IAsyncReader interface
- // from this object
- // Optional allocator should be proposed as a preferred allocator if
- // necessary
- // bSync is TRUE if we are to use sync reads instead of the
- // async methods.
- HRESULT Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync);
-
- // disconnect any connection made in Connect
- HRESULT Disconnect();
-
- // agree an allocator using RequestAllocator - optional
- // props param specifies your requirements (non-zero fields).
- // returns an error code if fail to match requirements.
- // optional IMemAllocator interface is offered as a preferred allocator
- // but no error occurs if it can't be met.
- virtual HRESULT DecideAllocator(
- IMemAllocator* pAlloc,
- ALLOCATOR_PROPERTIES * pProps);
-
- // set start and stop position. if active, will start immediately at
- // the new position. Default is 0 to duration
- HRESULT Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop);
-
- // return the total duration
- HRESULT Duration(REFERENCE_TIME* ptDuration);
-
- // start pulling data
- HRESULT Active(void);
-
- // stop pulling data
- HRESULT Inactive(void);
-
- // helper functions
- LONGLONG AlignDown(LONGLONG ll, LONG lAlign) {
- // aligning downwards is just truncation
- return ll & ~(lAlign-1);
- };
-
- LONGLONG AlignUp(LONGLONG ll, LONG lAlign) {
- // align up: round up to next boundary
- return (ll + (lAlign -1)) & ~(lAlign -1);
- };
-
- // GetReader returns the (addrefed) IAsyncReader interface
- // for SyncRead etc
- IAsyncReader* GetReader() {
- m_pReader->AddRef();
- return m_pReader;
- };
-
- // -- pure --
-
- // override this to handle data arrival
- // return value other than S_OK will stop data
- virtual HRESULT Receive(IMediaSample*) PURE;
-
- // override this to handle end-of-stream
- virtual HRESULT EndOfStream(void) PURE;
-
- // called on runtime errors that will have caused pulling
- // to stop
- // these errors are all returned from the upstream filter, who
- // will have already reported any errors to the filtergraph.
- virtual void OnError(HRESULT hr) PURE;
-
- // flush this pin and all downstream
- virtual HRESULT BeginFlush() PURE;
- virtual HRESULT EndFlush() PURE;
-
-};
-
-#endif //__PULLPIN_H__
+//------------------------------------------------------------------------------
+// File: PullPin.h
+//
+// Desc: DirectShow base classes - defines CPullPin class.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __PULLPIN_H__
+#define __PULLPIN_H__
+
+//
+// CPullPin
+//
+// object supporting pulling data from an IAsyncReader interface.
+// Given a start/stop position, calls a pure Receive method with each
+// IMediaSample received.
+//
+// This is essentially for use in a MemInputPin when it finds itself
+// connected to an IAsyncReader pin instead of a pushing pin.
+//
+
+class CPullPin : public CAMThread
+{
+ IAsyncReader* m_pReader;
+ REFERENCE_TIME m_tStart;
+ REFERENCE_TIME m_tStop;
+ REFERENCE_TIME m_tDuration;
+ BOOL m_bSync;
+
+ enum ThreadMsg {
+ TM_Pause, // stop pulling and wait for next message
+ TM_Start, // start pulling
+ TM_Exit, // stop and exit
+ };
+
+ ThreadMsg m_State;
+
+ // override pure thread proc from CAMThread
+ DWORD ThreadProc(void);
+
+ // running pull method (check m_bSync)
+ void Process(void);
+
+ // clean up any cancelled i/o after a flush
+ void CleanupCancelled(void);
+
+ // suspend thread from pulling, eg during seek
+ HRESULT PauseThread();
+
+ // start thread pulling - create thread if necy
+ HRESULT StartThread();
+
+ // stop and close thread
+ HRESULT StopThread();
+
+ // called from ProcessAsync to queue and collect requests
+ HRESULT QueueSample(
+ __inout REFERENCE_TIME& tCurrent,
+ REFERENCE_TIME tAlignStop,
+ BOOL bDiscontinuity);
+
+ HRESULT CollectAndDeliver(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop);
+
+ HRESULT DeliverSample(
+ IMediaSample* pSample,
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop);
+
+protected:
+ IMemAllocator * m_pAlloc;
+
+public:
+ CPullPin();
+ virtual ~CPullPin();
+
+ // returns S_OK if successfully connected to an IAsyncReader interface
+ // from this object
+ // Optional allocator should be proposed as a preferred allocator if
+ // necessary
+ // bSync is TRUE if we are to use sync reads instead of the
+ // async methods.
+ HRESULT Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync);
+
+ // disconnect any connection made in Connect
+ HRESULT Disconnect();
+
+ // agree an allocator using RequestAllocator - optional
+ // props param specifies your requirements (non-zero fields).
+ // returns an error code if fail to match requirements.
+ // optional IMemAllocator interface is offered as a preferred allocator
+ // but no error occurs if it can't be met.
+ virtual HRESULT DecideAllocator(
+ IMemAllocator* pAlloc,
+ __inout_opt ALLOCATOR_PROPERTIES * pProps);
+
+ // set start and stop position. if active, will start immediately at
+ // the new position. Default is 0 to duration
+ HRESULT Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop);
+
+ // return the total duration
+ HRESULT Duration(__out REFERENCE_TIME* ptDuration);
+
+ // start pulling data
+ HRESULT Active(void);
+
+ // stop pulling data
+ HRESULT Inactive(void);
+
+ // helper functions
+ LONGLONG AlignDown(LONGLONG ll, LONG lAlign) {
+ // aligning downwards is just truncation
+ return ll & ~(lAlign-1);
+ };
+
+ LONGLONG AlignUp(LONGLONG ll, LONG lAlign) {
+ // align up: round up to next boundary
+ return (ll + (lAlign -1)) & ~(lAlign -1);
+ };
+
+ // GetReader returns the (addrefed) IAsyncReader interface
+ // for SyncRead etc
+ IAsyncReader* GetReader() {
+ m_pReader->AddRef();
+ return m_pReader;
+ };
+
+ // -- pure --
+
+ // override this to handle data arrival
+ // return value other than S_OK will stop data
+ virtual HRESULT Receive(IMediaSample*) PURE;
+
+ // override this to handle end-of-stream
+ virtual HRESULT EndOfStream(void) PURE;
+
+ // called on runtime errors that will have caused pulling
+ // to stop
+ // these errors are all returned from the upstream filter, who
+ // will have already reported any errors to the filtergraph.
+ virtual void OnError(HRESULT hr) PURE;
+
+ // flush this pin and all downstream
+ virtual HRESULT BeginFlush() PURE;
+ virtual HRESULT EndFlush() PURE;
+
+};
+
+#endif //__PULLPIN_H__
diff --git a/dshow_base/refclock.cpp b/dshow_base/refclock.cpp
new file mode 100644
index 0000000..8ae25f4
--- /dev/null
+++ b/dshow_base/refclock.cpp
@@ -0,0 +1,402 @@
+//------------------------------------------------------------------------------
+// File: RefClock.cpp
+//
+// Desc: DirectShow base classes - implements the IReferenceClock interface.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#include
+
+#ifdef DXMPERF
+#include "dxmperf.h"
+#endif // DXMPERF
+
+
+// 'this' used in constructor list
+#pragma warning(disable:4355)
+
+
+STDMETHODIMP CBaseReferenceClock::NonDelegatingQueryInterface(
+ REFIID riid,
+ __deref_out void ** ppv)
+{
+ HRESULT hr;
+
+ if (riid == IID_IReferenceClock)
+ {
+ hr = GetInterface((IReferenceClock *) this, ppv);
+ }
+ else if (riid == IID_IReferenceClockTimerControl)
+ {
+ hr = GetInterface((IReferenceClockTimerControl *) this, ppv);
+ }
+ else
+ {
+ hr = CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+ return hr;
+}
+
+CBaseReferenceClock::~CBaseReferenceClock()
+{
+#ifdef DXMPERF
+ PERFLOG_DTOR( L"CBaseReferenceClock", (IReferenceClock *) this );
+#endif // DXMPERF
+
+ if (m_TimerResolution) timeEndPeriod(m_TimerResolution);
+
+ if (m_pSchedule)
+ {
+ m_pSchedule->DumpLinkedList();
+ }
+
+ if (m_hThread)
+ {
+ m_bAbort = TRUE;
+ TriggerThread();
+ WaitForSingleObject( m_hThread, INFINITE );
+ EXECUTE_ASSERT( CloseHandle(m_hThread) );
+ m_hThread = 0;
+ EXECUTE_ASSERT( CloseHandle(m_pSchedule->GetEvent()) );
+ delete m_pSchedule;
+ }
+}
+
+// A derived class may supply a hThreadEvent if it has its own thread that will take care
+// of calling the schedulers Advise method. (Refere to CBaseReferenceClock::AdviseThread()
+// to see what such a thread has to do.)
+CBaseReferenceClock::CBaseReferenceClock( __in_opt LPCTSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ __inout HRESULT *phr,
+ __inout_opt CAMSchedule * pShed )
+: CUnknown( pName, pUnk )
+, m_rtLastGotTime(0)
+, m_TimerResolution(0)
+, m_bAbort( FALSE )
+, m_pSchedule( pShed ? pShed : new CAMSchedule(CreateEvent(NULL, FALSE, FALSE, NULL)) )
+, m_hThread(0)
+{
+
+#ifdef DXMPERF
+ PERFLOG_CTOR( pName ? pName : L"CBaseReferenceClock", (IReferenceClock *) this );
+#endif // DXMPERF
+
+ ASSERT(m_pSchedule);
+ if (!m_pSchedule)
+ {
+ *phr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ // Set up the highest resolution timer we can manage
+ TIMECAPS tc;
+ m_TimerResolution = (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)))
+ ? tc.wPeriodMin
+ : 1;
+
+ timeBeginPeriod(m_TimerResolution);
+
+ /* Initialise our system times - the derived clock should set the right values */
+ m_dwPrevSystemTime = timeGetTime();
+ m_rtPrivateTime = (UNITS / MILLISECONDS) * m_dwPrevSystemTime;
+
+ #ifdef PERF
+ m_idGetSystemTime = MSR_REGISTER(TEXT("CBaseReferenceClock::GetTime"));
+ #endif
+
+ if ( !pShed )
+ {
+ DWORD ThreadID;
+ m_hThread = ::CreateThread(NULL, // Security attributes
+ (DWORD) 0, // Initial stack size
+ AdviseThreadFunction, // Thread start address
+ (LPVOID) this, // Thread parameter
+ (DWORD) 0, // Creation flags
+ &ThreadID); // Thread identifier
+
+ if (m_hThread)
+ {
+ SetThreadPriority( m_hThread, THREAD_PRIORITY_TIME_CRITICAL );
+ }
+ else
+ {
+ *phr = E_FAIL;
+ EXECUTE_ASSERT( CloseHandle(m_pSchedule->GetEvent()) );
+ delete m_pSchedule;
+ m_pSchedule = NULL;
+ }
+ }
+ }
+}
+
+void CBaseReferenceClock::Restart (IN REFERENCE_TIME rtMinTime)
+{
+ Lock();
+ m_rtLastGotTime = rtMinTime ;
+ Unlock();
+}
+
+STDMETHODIMP CBaseReferenceClock::GetTime(__out REFERENCE_TIME *pTime)
+{
+ HRESULT hr;
+ if (pTime)
+ {
+ REFERENCE_TIME rtNow;
+ Lock();
+ rtNow = GetPrivateTime();
+ if (rtNow > m_rtLastGotTime)
+ {
+ m_rtLastGotTime = rtNow;
+ hr = S_OK;
+ }
+ else
+ {
+ hr = S_FALSE;
+ }
+ *pTime = m_rtLastGotTime;
+ Unlock();
+ MSR_INTEGER(m_idGetSystemTime, LONG((*pTime) / (UNITS/MILLISECONDS)) );
+
+#ifdef DXMPERF
+ PERFLOG_GETTIME( (IReferenceClock *) this, *pTime );
+#endif // DXMPERF
+
+ }
+ else hr = E_POINTER;
+
+ return hr;
+}
+
+/* Ask for an async notification that a time has elapsed */
+
+STDMETHODIMP CBaseReferenceClock::AdviseTime(
+ REFERENCE_TIME baseTime, // base reference time
+ REFERENCE_TIME streamTime, // stream offset time
+ HEVENT hEvent, // advise via this event
+ __out DWORD_PTR *pdwAdviseCookie)// where your cookie goes
+{
+ CheckPointer(pdwAdviseCookie, E_POINTER);
+ *pdwAdviseCookie = 0;
+
+ // Check that the event is not already set
+ ASSERT(WAIT_TIMEOUT == WaitForSingleObject(HANDLE(hEvent),0));
+
+ HRESULT hr;
+
+ const REFERENCE_TIME lRefTime = baseTime + streamTime;
+ if ( lRefTime <= 0 || lRefTime == MAX_TIME )
+ {
+ hr = E_INVALIDARG;
+ }
+ else
+ {
+ *pdwAdviseCookie = m_pSchedule->AddAdvisePacket( lRefTime, 0, HANDLE(hEvent), FALSE );
+ hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;
+ }
+ return hr;
+}
+
+
+/* Ask for an asynchronous periodic notification that a time has elapsed */
+
+STDMETHODIMP CBaseReferenceClock::AdvisePeriodic(
+ REFERENCE_TIME StartTime, // starting at this time
+ REFERENCE_TIME PeriodTime, // time between notifications
+ HSEMAPHORE hSemaphore, // advise via a semaphore
+ __out DWORD_PTR *pdwAdviseCookie) // where your cookie goes
+{
+ CheckPointer(pdwAdviseCookie, E_POINTER);
+ *pdwAdviseCookie = 0;
+
+ HRESULT hr;
+ if (StartTime > 0 && PeriodTime > 0 && StartTime != MAX_TIME )
+ {
+ *pdwAdviseCookie = m_pSchedule->AddAdvisePacket( StartTime, PeriodTime, HANDLE(hSemaphore), TRUE );
+ hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;
+ }
+ else hr = E_INVALIDARG;
+
+ return hr;
+}
+
+
+STDMETHODIMP CBaseReferenceClock::Unadvise(DWORD_PTR dwAdviseCookie)
+{
+ return m_pSchedule->Unadvise(dwAdviseCookie);
+}
+
+
+REFERENCE_TIME CBaseReferenceClock::GetPrivateTime()
+{
+ CAutoLock cObjectLock(this);
+
+
+ /* If the clock has wrapped then the current time will be less than
+ * the last time we were notified so add on the extra milliseconds
+ *
+ * The time period is long enough so that the likelihood of
+ * successive calls spanning the clock cycle is not considered.
+ */
+
+ DWORD dwTime = timeGetTime();
+ {
+ m_rtPrivateTime += Int32x32To64(UNITS / MILLISECONDS, (DWORD)(dwTime - m_dwPrevSystemTime));
+ m_dwPrevSystemTime = dwTime;
+ }
+
+ return m_rtPrivateTime;
+}
+
+
+/* Adjust the current time by the input value. This allows an
+ external time source to work out some of the latency of the clock
+ system and adjust the "current" time accordingly. The intent is
+ that the time returned to the user is synchronised to a clock
+ source and allows drift to be catered for.
+
+ For example: if the clock source detects a drift it can pass a delta
+ to the current time rather than having to set an explicit time.
+*/
+
+STDMETHODIMP CBaseReferenceClock::SetTimeDelta(const REFERENCE_TIME & TimeDelta)
+{
+#ifdef DEBUG
+
+ // Just break if passed an improper time delta value
+ LONGLONG llDelta = TimeDelta > 0 ? TimeDelta : -TimeDelta;
+ if (llDelta > UNITS * 1000) {
+ DbgLog((LOG_TRACE, 0, TEXT("Bad Time Delta")));
+ //DebugBreak();
+ }
+
+ // We're going to calculate a "severity" for the time change. Max -1
+ // min 8. We'll then use this as the debug logging level for a
+ // debug log message.
+ const LONG usDelta = LONG(TimeDelta/10); // Delta in micro-secs
+
+ DWORD delta = abs(usDelta); // varying delta
+ // Severity == 8 - ceil(log(abs( micro-secs delta)))
+ int Severity = 8;
+ while ( delta > 0 )
+ {
+ delta >>= 3; // div 8
+ Severity--;
+ }
+
+ // Sev == 0 => > 2 second delta!
+ DbgLog((LOG_TIMING, Severity < 0 ? 0 : Severity,
+ TEXT("Sev %2i: CSystemClock::SetTimeDelta(%8ld us) %lu -> %lu ms."),
+ Severity, usDelta, DWORD(ConvertToMilliseconds(m_rtPrivateTime)),
+ DWORD(ConvertToMilliseconds(TimeDelta+m_rtPrivateTime)) ));
+
+ // Don't want the DbgBreak to fire when running stress on debug-builds.
+ #ifdef BREAK_ON_SEVERE_TIME_DELTA
+ if (Severity < 0)
+ DbgBreakPoint(TEXT("SetTimeDelta > 16 seconds!"),
+ TEXT(__FILE__),__LINE__);
+ #endif
+
+#endif
+
+ CAutoLock cObjectLock(this);
+ m_rtPrivateTime += TimeDelta;
+ // If time goes forwards, and we have advises, then we need to
+ // trigger the thread so that it can re-evaluate its wait time.
+ // Since we don't want the cost of the thread switches if the change
+ // is really small, only do it if clock goes forward by more than
+ // 0.5 millisecond. If the time goes backwards, the thread will
+ // wake up "early" (relativly speaking) and will re-evaluate at
+ // that time.
+ if ( TimeDelta > 5000 && m_pSchedule->GetAdviseCount() > 0 ) TriggerThread();
+ return NOERROR;
+}
+
+// Thread stuff
+
+DWORD __stdcall CBaseReferenceClock::AdviseThreadFunction(__in LPVOID p)
+{
+ return DWORD(reinterpret_cast(p)->AdviseThread());
+}
+
+HRESULT CBaseReferenceClock::AdviseThread()
+{
+ DWORD dwWait = INFINITE;
+
+ // The first thing we do is wait until something interesting happens
+ // (meaning a first advise or shutdown). This prevents us calling
+ // GetPrivateTime immediately which is goodness as that is a virtual
+ // routine and the derived class may not yet be constructed. (This
+ // thread is created in the base class constructor.)
+
+ while ( !m_bAbort )
+ {
+ // Wait for an interesting event to happen
+ DbgLog((LOG_TIMING, 3, TEXT("CBaseRefClock::AdviseThread() Delay: %lu ms"), dwWait ));
+ WaitForSingleObject(m_pSchedule->GetEvent(), dwWait);
+ if (m_bAbort) break;
+
+ // There are several reasons why we need to work from the internal
+ // time, mainly to do with what happens when time goes backwards.
+ // Mainly, it stop us looping madly if an event is just about to
+ // expire when the clock goes backward (i.e. GetTime stop for a
+ // while).
+ const REFERENCE_TIME rtNow = GetPrivateTime();
+
+ DbgLog((LOG_TIMING, 3,
+ TEXT("CBaseRefClock::AdviseThread() Woke at = %lu ms"),
+ ConvertToMilliseconds(rtNow) ));
+
+ // We must add in a millisecond, since this is the resolution of our
+ // WaitForSingleObject timer. Failure to do so will cause us to loop
+ // franticly for (approx) 1 a millisecond.
+ m_rtNextAdvise = m_pSchedule->Advise( 10000 + rtNow );
+ LONGLONG llWait = m_rtNextAdvise - rtNow;
+
+ ASSERT( llWait > 0 );
+
+ llWait = ConvertToMilliseconds(llWait);
+ // DON'T replace this with a max!! (The type's of these things is VERY important)
+ dwWait = (llWait > REFERENCE_TIME(UINT_MAX)) ? UINT_MAX : DWORD(llWait);
+ };
+ return NOERROR;
+}
+
+HRESULT CBaseReferenceClock::SetDefaultTimerResolution(
+ REFERENCE_TIME timerResolution // in 100ns
+ )
+{
+ CAutoLock cObjectLock(this);
+ if( 0 == timerResolution ) {
+ if( m_TimerResolution ) {
+ timeEndPeriod( m_TimerResolution );
+ m_TimerResolution = 0;
+ }
+ } else {
+ TIMECAPS tc;
+ DWORD dwMinResolution = (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)))
+ ? tc.wPeriodMin
+ : 1;
+ DWORD dwResolution = max( dwMinResolution, DWORD(timerResolution / 10000) );
+ if( dwResolution != m_TimerResolution ) {
+ timeEndPeriod(m_TimerResolution);
+ m_TimerResolution = dwResolution;
+ timeBeginPeriod( m_TimerResolution );
+ }
+ }
+ return S_OK;
+}
+
+HRESULT CBaseReferenceClock::GetDefaultTimerResolution(
+ __out REFERENCE_TIME* pTimerResolution // in 100ns
+ )
+{
+ if( !pTimerResolution ) {
+ return E_POINTER;
+ }
+ CAutoLock cObjectLock(this);
+ *pTimerResolution = m_TimerResolution * 10000;
+ return S_OK;
+}
diff --git a/dshow_base/refclock.h b/dshow_base/refclock.h
index a47ae7b..d2b0bb1 100644
--- a/dshow_base/refclock.h
+++ b/dshow_base/refclock.h
@@ -1,171 +1,184 @@
-//------------------------------------------------------------------------------
-// File: RefClock.h
-//
-// Desc: DirectShow base classes - defines the IReferenceClock interface.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __BASEREFCLOCK__
-#define __BASEREFCLOCK__
-
-#include "dsschedule.h"
-
-const UINT RESOLUTION = 1; /* High resolution timer */
-const INT ADVISE_CACHE = 4; /* Default cache size */
-const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF; /* Maximum LONGLONG value */
-
-inline LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME& RT)
-{
- /* This converts an arbitrary value representing a reference time
- into a MILLISECONDS value for use in subsequent system calls */
-
- return (RT / (UNITS / MILLISECONDS));
-}
-
-/* This class hierarchy will support an IReferenceClock interface so
- that an audio card (or other externally driven clock) can update the
- system wide clock that everyone uses.
-
- The interface will be pretty thin with probably just one update method
- This interface has not yet been defined.
- */
-
-/* This abstract base class implements the IReferenceClock
- * interface. Classes that actually provide clock signals (from
- * whatever source) have to be derived from this class.
- *
- * The abstract class provides implementations for:
- * CUnknown support
- * locking support (CCritSec)
- * client advise code (creates a thread)
- *
- * Question: what can we do about quality? Change the timer
- * resolution to lower the system load? Up the priority of the
- * timer thread to force more responsive signals?
- *
- * During class construction we create a worker thread that is destroyed during
- * destuction. This thread executes a series of WaitForSingleObject calls,
- * waking up when a command is given to the thread or the next wake up point
- * is reached. The wakeup points are determined by clients making Advise
- * calls.
- *
- * Each advise call defines a point in time when they wish to be notified. A
- * periodic advise is a series of these such events. We maintain a list of
- * advise links and calculate when the nearest event notification is due for.
- * We then call WaitForSingleObject with a timeout equal to this time. The
- * handle we wait on is used by the class to signal that something has changed
- * and that we must reschedule the next event. This typically happens when
- * someone comes in and asks for an advise link while we are waiting for an
- * event to timeout.
- *
- * While we are modifying the list of advise requests we
- * are protected from interference through a critical section. Clients are NOT
- * advised through callbacks. One shot clients have an event set, while
- * periodic clients have a semaphore released for each event notification. A
- * semaphore allows a client to be kept up to date with the number of events
- * actually triggered and be assured that they can't miss multiple events being
- * set.
- *
- * Keeping track of advises is taken care of by the CAMSchedule class.
- */
-
-class CBaseReferenceClock
-: public CUnknown, public IReferenceClock, public CCritSec
-{
-protected:
- virtual ~CBaseReferenceClock(); // Don't let me be created on the stack!
-public:
- CBaseReferenceClock(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr, CAMSchedule * pSched = 0 );
-
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void ** ppv);
-
- DECLARE_IUNKNOWN
-
- /* IReferenceClock methods */
- // Derived classes must implement GetPrivateTime(). All our GetTime
- // does is call GetPrivateTime and then check so that time does not
- // go backwards. A return code of S_FALSE implies that the internal
- // clock has gone backwards and GetTime time has halted until internal
- // time has caught up. (Don't know if this will be much use to folk,
- // but it seems odd not to use the return code for something useful.)
- STDMETHODIMP GetTime(REFERENCE_TIME *pTime);
- // When this is called, it sets m_rtLastGotTime to the time it returns.
-
- /* Provide standard mechanisms for scheduling events */
-
- /* Ask for an async notification that a time has elapsed */
- STDMETHODIMP AdviseTime(
- REFERENCE_TIME baseTime, // base reference time
- REFERENCE_TIME streamTime, // stream offset time
- HEVENT hEvent, // advise via this event
- DWORD_PTR *pdwAdviseCookie // where your cookie goes
- );
-
- /* Ask for an asynchronous periodic notification that a time has elapsed */
- STDMETHODIMP AdvisePeriodic(
- REFERENCE_TIME StartTime, // starting at this time
- REFERENCE_TIME PeriodTime, // time between notifications
- HSEMAPHORE hSemaphore, // advise via a semaphore
- DWORD_PTR *pdwAdviseCookie // where your cookie goes
- );
-
- /* Cancel a request for notification(s) - if the notification was
- * a one shot timer then this function doesn't need to be called
- * as the advise is automatically cancelled, however it does no
- * harm to explicitly cancel a one-shot advise. It is REQUIRED that
- * clients call Unadvise to clear a Periodic advise setting.
- */
-
- STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie);
-
- /* Methods for the benefit of derived classes or outer objects */
-
- // GetPrivateTime() is the REAL clock. GetTime is just a cover for
- // it. Derived classes will probably override this method but not
- // GetTime() itself.
- // The important point about GetPrivateTime() is it's allowed to go
- // backwards. Our GetTime() will keep returning the LastGotTime
- // until GetPrivateTime() catches up.
- virtual REFERENCE_TIME GetPrivateTime();
-
- /* Provide a method for correcting drift */
- STDMETHODIMP SetTimeDelta( const REFERENCE_TIME& TimeDelta );
-
- CAMSchedule * GetSchedule() const { return m_pSchedule; }
-
-private:
- REFERENCE_TIME m_rtPrivateTime; // Current best estimate of time
- DWORD m_dwPrevSystemTime; // Last vaule we got from timeGetTime
- REFERENCE_TIME m_rtLastGotTime; // Last time returned by GetTime
- REFERENCE_TIME m_rtNextAdvise; // Time of next advise
- UINT m_TimerResolution;
-
-#ifdef PERF
- int m_idGetSystemTime;
-#endif
-
-// Thread stuff
-public:
- void TriggerThread() // Wakes thread up. Need to do this if
- { // time to next advise needs reevaluating.
- EXECUTE_ASSERT(SetEvent(m_pSchedule->GetEvent()));
- }
-
-
-private:
- BOOL m_bAbort; // Flag used for thread shutdown
- HANDLE m_hThread; // Thread handle
-
- HRESULT AdviseThread(); // Method in which the advise thread runs
- static DWORD __stdcall AdviseThreadFunction(LPVOID); // Function used to get there
-
-protected:
- CAMSchedule * const m_pSchedule;
-
- void Restart (IN REFERENCE_TIME rtMinTime = 0I64) ;
-};
-
-#endif
-
+//------------------------------------------------------------------------------
+// File: RefClock.h
+//
+// Desc: DirectShow base classes - defines the IReferenceClock interface.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __BASEREFCLOCK__
+#define __BASEREFCLOCK__
+
+#include
+
+const UINT RESOLUTION = 1; /* High resolution timer */
+const INT ADVISE_CACHE = 4; /* Default cache size */
+const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF; /* Maximum LONGLONG value */
+
+inline LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME& RT)
+{
+ /* This converts an arbitrary value representing a reference time
+ into a MILLISECONDS value for use in subsequent system calls */
+
+ return (RT / (UNITS / MILLISECONDS));
+}
+
+/* This class hierarchy will support an IReferenceClock interface so
+ that an audio card (or other externally driven clock) can update the
+ system wide clock that everyone uses.
+
+ The interface will be pretty thin with probably just one update method
+ This interface has not yet been defined.
+ */
+
+/* This abstract base class implements the IReferenceClock
+ * interface. Classes that actually provide clock signals (from
+ * whatever source) have to be derived from this class.
+ *
+ * The abstract class provides implementations for:
+ * CUnknown support
+ * locking support (CCritSec)
+ * client advise code (creates a thread)
+ *
+ * Question: what can we do about quality? Change the timer
+ * resolution to lower the system load? Up the priority of the
+ * timer thread to force more responsive signals?
+ *
+ * During class construction we create a worker thread that is destroyed during
+ * destuction. This thread executes a series of WaitForSingleObject calls,
+ * waking up when a command is given to the thread or the next wake up point
+ * is reached. The wakeup points are determined by clients making Advise
+ * calls.
+ *
+ * Each advise call defines a point in time when they wish to be notified. A
+ * periodic advise is a series of these such events. We maintain a list of
+ * advise links and calculate when the nearest event notification is due for.
+ * We then call WaitForSingleObject with a timeout equal to this time. The
+ * handle we wait on is used by the class to signal that something has changed
+ * and that we must reschedule the next event. This typically happens when
+ * someone comes in and asks for an advise link while we are waiting for an
+ * event to timeout.
+ *
+ * While we are modifying the list of advise requests we
+ * are protected from interference through a critical section. Clients are NOT
+ * advised through callbacks. One shot clients have an event set, while
+ * periodic clients have a semaphore released for each event notification. A
+ * semaphore allows a client to be kept up to date with the number of events
+ * actually triggered and be assured that they can't miss multiple events being
+ * set.
+ *
+ * Keeping track of advises is taken care of by the CAMSchedule class.
+ */
+
+class CBaseReferenceClock
+: public CUnknown, public IReferenceClock, public CCritSec, public IReferenceClockTimerControl
+{
+protected:
+ virtual ~CBaseReferenceClock(); // Don't let me be created on the stack!
+public:
+ CBaseReferenceClock(__in_opt LPCTSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ __inout HRESULT *phr,
+ __inout_opt CAMSchedule * pSched = 0 );
+
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
+
+ DECLARE_IUNKNOWN
+
+ /* IReferenceClock methods */
+ // Derived classes must implement GetPrivateTime(). All our GetTime
+ // does is call GetPrivateTime and then check so that time does not
+ // go backwards. A return code of S_FALSE implies that the internal
+ // clock has gone backwards and GetTime time has halted until internal
+ // time has caught up. (Don't know if this will be much use to folk,
+ // but it seems odd not to use the return code for something useful.)
+ STDMETHODIMP GetTime(__out REFERENCE_TIME *pTime);
+ // When this is called, it sets m_rtLastGotTime to the time it returns.
+
+ /* Provide standard mechanisms for scheduling events */
+
+ /* Ask for an async notification that a time has elapsed */
+ STDMETHODIMP AdviseTime(
+ REFERENCE_TIME baseTime, // base reference time
+ REFERENCE_TIME streamTime, // stream offset time
+ HEVENT hEvent, // advise via this event
+ __out DWORD_PTR *pdwAdviseCookie// where your cookie goes
+ );
+
+ /* Ask for an asynchronous periodic notification that a time has elapsed */
+ STDMETHODIMP AdvisePeriodic(
+ REFERENCE_TIME StartTime, // starting at this time
+ REFERENCE_TIME PeriodTime, // time between notifications
+ HSEMAPHORE hSemaphore, // advise via a semaphore
+ __out DWORD_PTR *pdwAdviseCookie// where your cookie goes
+ );
+
+ /* Cancel a request for notification(s) - if the notification was
+ * a one shot timer then this function doesn't need to be called
+ * as the advise is automatically cancelled, however it does no
+ * harm to explicitly cancel a one-shot advise. It is REQUIRED that
+ * clients call Unadvise to clear a Periodic advise setting.
+ */
+
+ STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie);
+
+ /* Methods for the benefit of derived classes or outer objects */
+
+ // GetPrivateTime() is the REAL clock. GetTime is just a cover for
+ // it. Derived classes will probably override this method but not
+ // GetTime() itself.
+ // The important point about GetPrivateTime() is it's allowed to go
+ // backwards. Our GetTime() will keep returning the LastGotTime
+ // until GetPrivateTime() catches up.
+ virtual REFERENCE_TIME GetPrivateTime();
+
+ /* Provide a method for correcting drift */
+ STDMETHODIMP SetTimeDelta( const REFERENCE_TIME& TimeDelta );
+
+ CAMSchedule * GetSchedule() const { return m_pSchedule; }
+
+ // IReferenceClockTimerControl methods
+ //
+ // Setting a default of 0 disables the default of 1ms
+ STDMETHODIMP SetDefaultTimerResolution(
+ REFERENCE_TIME timerResolution // in 100ns
+ );
+ STDMETHODIMP GetDefaultTimerResolution(
+ __out REFERENCE_TIME* pTimerResolution // in 100ns
+ );
+
+private:
+ REFERENCE_TIME m_rtPrivateTime; // Current best estimate of time
+ DWORD m_dwPrevSystemTime; // Last vaule we got from timeGetTime
+ REFERENCE_TIME m_rtLastGotTime; // Last time returned by GetTime
+ REFERENCE_TIME m_rtNextAdvise; // Time of next advise
+ UINT m_TimerResolution;
+
+#ifdef PERF
+ int m_idGetSystemTime;
+#endif
+
+// Thread stuff
+public:
+ void TriggerThread() // Wakes thread up. Need to do this if
+ { // time to next advise needs reevaluating.
+ EXECUTE_ASSERT(SetEvent(m_pSchedule->GetEvent()));
+ }
+
+
+private:
+ BOOL m_bAbort; // Flag used for thread shutdown
+ HANDLE m_hThread; // Thread handle
+
+ HRESULT AdviseThread(); // Method in which the advise thread runs
+ static DWORD __stdcall AdviseThreadFunction(__in LPVOID); // Function used to get there
+
+protected:
+ CAMSchedule * m_pSchedule;
+
+ void Restart (IN REFERENCE_TIME rtMinTime = 0I64) ;
+};
+
+#endif
+
diff --git a/dshow_base/reftime.h b/dshow_base/reftime.h
index 2cf70df..5bc99a6 100644
--- a/dshow_base/reftime.h
+++ b/dshow_base/reftime.h
@@ -1,116 +1,116 @@
-//------------------------------------------------------------------------------
-// File: RefTime.h
-//
-// Desc: DirectShow base classes - defines CRefTime, a class that manages
-// reference times.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-//
-// CRefTime
-//
-// Manage reference times.
-// Shares same data layout as REFERENCE_TIME, but adds some (nonvirtual)
-// functions providing simple comparison, conversion and arithmetic.
-//
-// A reference time (at the moment) is a unit of seconds represented in
-// 100ns units as is used in the Win32 FILETIME structure. BUT the time
-// a REFERENCE_TIME represents is NOT the time elapsed since 1/1/1601 it
-// will either be stream time or reference time depending upon context
-//
-// This class provides simple arithmetic operations on reference times
-//
-// keep non-virtual otherwise the data layout will not be the same as
-// REFERENCE_TIME
-
-
-// -----
-// note that you are safe to cast a CRefTime* to a REFERENCE_TIME*, but
-// you will need to do so explicitly
-// -----
-
-
-#ifndef __REFTIME__
-#define __REFTIME__
-
-
-const LONGLONG MILLISECONDS = (1000); // 10 ^ 3
-const LONGLONG NANOSECONDS = (1000000000); // 10 ^ 9
-const LONGLONG UNITS = (NANOSECONDS / 100); // 10 ^ 7
-
-/* Unfortunately an inline function here generates a call to __allmul
- - even for constants!
-*/
-#define MILLISECONDS_TO_100NS_UNITS(lMs) \
- Int32x32To64((lMs), (UNITS / MILLISECONDS))
-
-class CRefTime
-{
-public:
-
- // *MUST* be the only data member so that this class is exactly
- // equivalent to a REFERENCE_TIME.
- // Also, must be *no virtual functions*
-
- REFERENCE_TIME m_time;
-
- inline CRefTime()
- {
- // default to 0 time
- m_time = 0;
- };
-
- inline CRefTime(LONG msecs)
- {
- m_time = MILLISECONDS_TO_100NS_UNITS(msecs);
- };
-
- inline CRefTime(REFERENCE_TIME rt)
- {
- m_time = rt;
- };
-
- inline operator REFERENCE_TIME() const
- {
- return m_time;
- };
-
- inline CRefTime& operator=(const CRefTime& rt)
- {
- m_time = rt.m_time;
- return *this;
- };
-
- inline CRefTime& operator=(const LONGLONG ll)
- {
- m_time = ll;
- return *this;
- };
-
- inline CRefTime& operator+=(const CRefTime& rt)
- {
- return (*this = *this + rt);
- };
-
- inline CRefTime& operator-=(const CRefTime& rt)
- {
- return (*this = *this - rt);
- };
-
- inline LONG Millisecs(void)
- {
- return (LONG)(m_time / (UNITS / MILLISECONDS));
- };
-
- inline LONGLONG GetUnits(void)
- {
- return m_time;
- };
-};
-
-const LONGLONG TimeZero = 0;
-
-#endif /* __REFTIME__ */
-
+//------------------------------------------------------------------------------
+// File: RefTime.h
+//
+// Desc: DirectShow base classes - defines CRefTime, a class that manages
+// reference times.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+//
+// CRefTime
+//
+// Manage reference times.
+// Shares same data layout as REFERENCE_TIME, but adds some (nonvirtual)
+// functions providing simple comparison, conversion and arithmetic.
+//
+// A reference time (at the moment) is a unit of seconds represented in
+// 100ns units as is used in the Win32 FILETIME structure. BUT the time
+// a REFERENCE_TIME represents is NOT the time elapsed since 1/1/1601 it
+// will either be stream time or reference time depending upon context
+//
+// This class provides simple arithmetic operations on reference times
+//
+// keep non-virtual otherwise the data layout will not be the same as
+// REFERENCE_TIME
+
+
+// -----
+// note that you are safe to cast a CRefTime* to a REFERENCE_TIME*, but
+// you will need to do so explicitly
+// -----
+
+
+#ifndef __REFTIME__
+#define __REFTIME__
+
+
+const LONGLONG MILLISECONDS = (1000); // 10 ^ 3
+const LONGLONG NANOSECONDS = (1000000000); // 10 ^ 9
+const LONGLONG UNITS = (NANOSECONDS / 100); // 10 ^ 7
+
+/* Unfortunately an inline function here generates a call to __allmul
+ - even for constants!
+*/
+#define MILLISECONDS_TO_100NS_UNITS(lMs) \
+ Int32x32To64((lMs), (UNITS / MILLISECONDS))
+
+class CRefTime
+{
+public:
+
+ // *MUST* be the only data member so that this class is exactly
+ // equivalent to a REFERENCE_TIME.
+ // Also, must be *no virtual functions*
+
+ REFERENCE_TIME m_time;
+
+ inline CRefTime()
+ {
+ // default to 0 time
+ m_time = 0;
+ };
+
+ inline CRefTime(LONG msecs)
+ {
+ m_time = MILLISECONDS_TO_100NS_UNITS(msecs);
+ };
+
+ inline CRefTime(REFERENCE_TIME rt)
+ {
+ m_time = rt;
+ };
+
+ inline operator REFERENCE_TIME() const
+ {
+ return m_time;
+ };
+
+ inline CRefTime& operator=(const CRefTime& rt)
+ {
+ m_time = rt.m_time;
+ return *this;
+ };
+
+ inline CRefTime& operator=(const LONGLONG ll)
+ {
+ m_time = ll;
+ return *this;
+ };
+
+ inline CRefTime& operator+=(const CRefTime& rt)
+ {
+ return (*this = *this + rt);
+ };
+
+ inline CRefTime& operator-=(const CRefTime& rt)
+ {
+ return (*this = *this - rt);
+ };
+
+ inline LONG Millisecs(void)
+ {
+ return (LONG)(m_time / (UNITS / MILLISECONDS));
+ };
+
+ inline LONGLONG GetUnits(void)
+ {
+ return m_time;
+ };
+};
+
+const LONGLONG TimeZero = 0;
+
+#endif /* __REFTIME__ */
+
diff --git a/dshow_base/renbase.cpp b/dshow_base/renbase.cpp
new file mode 100644
index 0000000..c6e1962
--- /dev/null
+++ b/dshow_base/renbase.cpp
@@ -0,0 +1,2858 @@
+//------------------------------------------------------------------------------
+// File: RenBase.cpp
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include // DirectShow base class definitions
+#include // Needed for definition of timeGetTime
+#include // Standard data type limit definitions
+#include // Used for time critical log functions
+
+#pragma warning(disable:4355)
+
+// Helper function for clamping time differences
+int inline TimeDiff(REFERENCE_TIME rt)
+{
+ if (rt < - (50 * UNITS)) {
+ return -(50 * UNITS);
+ } else
+ if (rt > 50 * UNITS) {
+ return 50 * UNITS;
+ } else return (int)rt;
+}
+
+// Implements the CBaseRenderer class
+
+CBaseRenderer::CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer
+ __in_opt LPCTSTR pName, // Debug ONLY description
+ __inout_opt LPUNKNOWN pUnk, // Aggregated owner object
+ __inout HRESULT *phr) : // General OLE return code
+
+ CBaseFilter(pName,pUnk,&m_InterfaceLock,RenderClass),
+ m_evComplete(TRUE, phr),
+ m_RenderEvent(FALSE, phr),
+ m_bAbort(FALSE),
+ m_pPosition(NULL),
+ m_ThreadSignal(TRUE, phr),
+ m_bStreaming(FALSE),
+ m_bEOS(FALSE),
+ m_bEOSDelivered(FALSE),
+ m_pMediaSample(NULL),
+ m_dwAdvise(0),
+ m_pQSink(NULL),
+ m_pInputPin(NULL),
+ m_bRepaintStatus(TRUE),
+ m_SignalTime(0),
+ m_bInReceive(FALSE),
+ m_EndOfStreamTimer(0)
+{
+ if (SUCCEEDED(*phr)) {
+ Ready();
+#ifdef PERF
+ m_idBaseStamp = MSR_REGISTER(TEXT("BaseRenderer: sample time stamp"));
+ m_idBaseRenderTime = MSR_REGISTER(TEXT("BaseRenderer: draw time (msec)"));
+ m_idBaseAccuracy = MSR_REGISTER(TEXT("BaseRenderer: Accuracy (msec)"));
+#endif
+ }
+}
+
+
+// Delete the dynamically allocated IMediaPosition and IMediaSeeking helper
+// object. The object is created when somebody queries us. These are standard
+// control interfaces for seeking and setting start/stop positions and rates.
+// We will probably also have made an input pin based on CRendererInputPin
+// that has to be deleted, it's created when an enumerator calls our GetPin
+
+CBaseRenderer::~CBaseRenderer()
+{
+ ASSERT(m_bStreaming == FALSE);
+ ASSERT(m_EndOfStreamTimer == 0);
+ StopStreaming();
+ ClearPendingSample();
+
+ // Delete any IMediaPosition implementation
+
+ if (m_pPosition) {
+ delete m_pPosition;
+ m_pPosition = NULL;
+ }
+
+ // Delete any input pin created
+
+ if (m_pInputPin) {
+ delete m_pInputPin;
+ m_pInputPin = NULL;
+ }
+
+ // Release any Quality sink
+
+ ASSERT(m_pQSink == NULL);
+}
+
+
+// This returns the IMediaPosition and IMediaSeeking interfaces
+
+HRESULT CBaseRenderer::GetMediaPositionInterface(REFIID riid, __deref_out void **ppv)
+{
+ CAutoLock cObjectCreationLock(&m_ObjectCreationLock);
+ if (m_pPosition) {
+ return m_pPosition->NonDelegatingQueryInterface(riid,ppv);
+ }
+
+ CBasePin *pPin = GetPin(0);
+ if (NULL == pPin) {
+ return E_OUTOFMEMORY;
+ }
+
+ HRESULT hr = NOERROR;
+
+ // Create implementation of this dynamically since sometimes we may
+ // never try and do a seek. The helper object implements a position
+ // control interface (IMediaPosition) which in fact simply takes the
+ // calls normally from the filter graph and passes them upstream
+
+ m_pPosition = new CRendererPosPassThru(NAME("Renderer CPosPassThru"),
+ CBaseFilter::GetOwner(),
+ (HRESULT *) &hr,
+ pPin);
+ if (m_pPosition == NULL) {
+ return E_OUTOFMEMORY;
+ }
+
+ if (FAILED(hr)) {
+ delete m_pPosition;
+ m_pPosition = NULL;
+ return E_NOINTERFACE;
+ }
+ return GetMediaPositionInterface(riid,ppv);
+}
+
+
+// Overriden to say what interfaces we support and where
+
+STDMETHODIMP CBaseRenderer::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ // Do we have this interface
+
+ if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {
+ return GetMediaPositionInterface(riid,ppv);
+ } else {
+ return CBaseFilter::NonDelegatingQueryInterface(riid,ppv);
+ }
+}
+
+
+// This is called whenever we change states, we have a manual reset event that
+// is signalled whenever we don't won't the source filter thread to wait in us
+// (such as in a stopped state) and likewise is not signalled whenever it can
+// wait (during paused and running) this function sets or resets the thread
+// event. The event is used to stop source filter threads waiting in Receive
+
+HRESULT CBaseRenderer::SourceThreadCanWait(BOOL bCanWait)
+{
+ if (bCanWait == TRUE) {
+ m_ThreadSignal.Reset();
+ } else {
+ m_ThreadSignal.Set();
+ }
+ return NOERROR;
+}
+
+
+#ifdef DEBUG
+// Dump the current renderer state to the debug terminal. The hardest part of
+// the renderer is the window where we unlock everything to wait for a clock
+// to signal it is time to draw or for the application to cancel everything
+// by stopping the filter. If we get things wrong we can leave the thread in
+// WaitForRenderTime with no way for it to ever get out and we will deadlock
+
+void CBaseRenderer::DisplayRendererState()
+{
+ DbgLog((LOG_TIMING, 1, TEXT("\nTimed out in WaitForRenderTime")));
+
+ // No way should this be signalled at this point
+
+ BOOL bSignalled = m_ThreadSignal.Check();
+ DbgLog((LOG_TIMING, 1, TEXT("Signal sanity check %d"),bSignalled));
+
+ // Now output the current renderer state variables
+
+ DbgLog((LOG_TIMING, 1, TEXT("Filter state %d"),m_State));
+
+ DbgLog((LOG_TIMING, 1, TEXT("Abort flag %d"),m_bAbort));
+
+ DbgLog((LOG_TIMING, 1, TEXT("Streaming flag %d"),m_bStreaming));
+
+ DbgLog((LOG_TIMING, 1, TEXT("Clock advise link %d"),m_dwAdvise));
+
+ DbgLog((LOG_TIMING, 1, TEXT("Current media sample %x"),m_pMediaSample));
+
+ DbgLog((LOG_TIMING, 1, TEXT("EOS signalled %d"),m_bEOS));
+
+ DbgLog((LOG_TIMING, 1, TEXT("EOS delivered %d"),m_bEOSDelivered));
+
+ DbgLog((LOG_TIMING, 1, TEXT("Repaint status %d"),m_bRepaintStatus));
+
+
+ // Output the delayed end of stream timer information
+
+ DbgLog((LOG_TIMING, 1, TEXT("End of stream timer %x"),m_EndOfStreamTimer));
+
+ DbgLog((LOG_TIMING, 1, TEXT("Deliver time %s"),CDisp((LONGLONG)m_SignalTime)));
+
+
+ // Should never timeout during a flushing state
+
+ BOOL bFlushing = m_pInputPin->IsFlushing();
+ DbgLog((LOG_TIMING, 1, TEXT("Flushing sanity check %d"),bFlushing));
+
+ // Display the time we were told to start at
+ DbgLog((LOG_TIMING, 1, TEXT("Last run time %s"),CDisp((LONGLONG)m_tStart.m_time)));
+
+ // Have we got a reference clock
+ if (m_pClock == NULL) return;
+
+ // Get the current time from the wall clock
+
+ CRefTime CurrentTime,StartTime,EndTime;
+ m_pClock->GetTime((REFERENCE_TIME*) &CurrentTime);
+ CRefTime Offset = CurrentTime - m_tStart;
+
+ // Display the current time from the clock
+
+ DbgLog((LOG_TIMING, 1, TEXT("Clock time %s"),CDisp((LONGLONG)CurrentTime.m_time)));
+
+ DbgLog((LOG_TIMING, 1, TEXT("Time difference %dms"),Offset.Millisecs()));
+
+
+ // Do we have a sample ready to render
+ if (m_pMediaSample == NULL) return;
+
+ m_pMediaSample->GetTime((REFERENCE_TIME*)&StartTime, (REFERENCE_TIME*)&EndTime);
+ DbgLog((LOG_TIMING, 1, TEXT("Next sample stream times (Start %d End %d ms)"),
+ StartTime.Millisecs(),EndTime.Millisecs()));
+
+ // Calculate how long it is until it is due for rendering
+ CRefTime Wait = (m_tStart + StartTime) - CurrentTime;
+ DbgLog((LOG_TIMING, 1, TEXT("Wait required %d ms"),Wait.Millisecs()));
+}
+#endif
+
+
+// Wait until the clock sets the timer event or we're otherwise signalled. We
+// set an arbitrary timeout for this wait and if it fires then we display the
+// current renderer state on the debugger. It will often fire if the filter's
+// left paused in an application however it may also fire during stress tests
+// if the synchronisation with application seeks and state changes is faulty
+
+#define RENDER_TIMEOUT 10000
+
+HRESULT CBaseRenderer::WaitForRenderTime()
+{
+ HANDLE WaitObjects[] = { m_ThreadSignal, m_RenderEvent };
+ DWORD Result = WAIT_TIMEOUT;
+
+ // Wait for either the time to arrive or for us to be stopped
+
+ OnWaitStart();
+ while (Result == WAIT_TIMEOUT) {
+ Result = WaitForMultipleObjects(2,WaitObjects,FALSE,RENDER_TIMEOUT);
+
+#ifdef DEBUG
+ if (Result == WAIT_TIMEOUT) DisplayRendererState();
+#endif
+
+ }
+ OnWaitEnd();
+
+ // We may have been awoken without the timer firing
+
+ if (Result == WAIT_OBJECT_0) {
+ return VFW_E_STATE_CHANGED;
+ }
+
+ SignalTimerFired();
+ return NOERROR;
+}
+
+
+// Poll waiting for Receive to complete. This really matters when
+// Receive may set the palette and cause window messages
+// The problem is that if we don't really wait for a renderer to
+// stop processing we can deadlock waiting for a transform which
+// is calling the renderer's Receive() method because the transform's
+// Stop method doesn't know to process window messages to unblock
+// the renderer's Receive processing
+void CBaseRenderer::WaitForReceiveToComplete()
+{
+ for (;;) {
+ if (!m_bInReceive) {
+ break;
+ }
+
+ MSG msg;
+ // Receive all interthread snedmessages
+ PeekMessage(&msg, NULL, WM_NULL, WM_NULL, PM_NOREMOVE);
+
+ Sleep(1);
+ }
+
+ // If the wakebit for QS_POSTMESSAGE is set, the PeekMessage call
+ // above just cleared the changebit which will cause some messaging
+ // calls to block (waitMessage, MsgWaitFor...) now.
+ // Post a dummy message to set the QS_POSTMESSAGE bit again
+ if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {
+ // Send dummy message
+ PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0);
+ }
+}
+
+// A filter can have four discrete states, namely Stopped, Running, Paused,
+// Intermediate. We are in an intermediate state if we are currently trying
+// to pause but haven't yet got the first sample (or if we have been flushed
+// in paused state and therefore still have to wait for a sample to arrive)
+
+// This class contains an event called m_evComplete which is signalled when
+// the current state is completed and is not signalled when we are waiting to
+// complete the last state transition. As mentioned above the only time we
+// use this at the moment is when we wait for a media sample in paused state
+// If while we are waiting we receive an end of stream notification from the
+// source filter then we know no data is imminent so we can reset the event
+// This means that when we transition to paused the source filter must call
+// end of stream on us or send us an image otherwise we'll hang indefinately
+
+
+// Simple internal way of getting the real state
+
+FILTER_STATE CBaseRenderer::GetRealState() {
+ return m_State;
+}
+
+
+// The renderer doesn't complete the full transition to paused states until
+// it has got one media sample to render. If you ask it for its state while
+// it's waiting it will return the state along with VFW_S_STATE_INTERMEDIATE
+
+STDMETHODIMP CBaseRenderer::GetState(DWORD dwMSecs,FILTER_STATE *State)
+{
+ CheckPointer(State,E_POINTER);
+
+ if (WaitDispatchingMessages(m_evComplete, dwMSecs) == WAIT_TIMEOUT) {
+ *State = m_State;
+ return VFW_S_STATE_INTERMEDIATE;
+ }
+ *State = m_State;
+ return NOERROR;
+}
+
+
+// If we're pausing and we have no samples we don't complete the transition
+// to State_Paused and we return S_FALSE. However if the m_bAbort flag has
+// been set then all samples are rejected so there is no point waiting for
+// one. If we do have a sample then return NOERROR. We will only ever return
+// VFW_S_STATE_INTERMEDIATE from GetState after being paused with no sample
+// (calling GetState after either being stopped or Run will NOT return this)
+
+HRESULT CBaseRenderer::CompleteStateChange(FILTER_STATE OldState)
+{
+ // Allow us to be paused when disconnected
+
+ if (m_pInputPin->IsConnected() == FALSE) {
+ Ready();
+ return S_OK;
+ }
+
+ // Have we run off the end of stream
+
+ if (IsEndOfStream() == TRUE) {
+ Ready();
+ return S_OK;
+ }
+
+ // Make sure we get fresh data after being stopped
+
+ if (HaveCurrentSample() == TRUE) {
+ if (OldState != State_Stopped) {
+ Ready();
+ return S_OK;
+ }
+ }
+ NotReady();
+ return S_FALSE;
+}
+
+
+// When we stop the filter the things we do are:-
+
+// Decommit the allocator being used in the connection
+// Release the source filter if it's waiting in Receive
+// Cancel any advise link we set up with the clock
+// Any end of stream signalled is now obsolete so reset
+// Allow us to be stopped when we are not connected
+
+STDMETHODIMP CBaseRenderer::Stop()
+{
+ CAutoLock cRendererLock(&m_InterfaceLock);
+
+ // Make sure there really is a state change
+
+ if (m_State == State_Stopped) {
+ return NOERROR;
+ }
+
+ // Is our input pin connected
+
+ if (m_pInputPin->IsConnected() == FALSE) {
+ NOTE("Input pin is not connected");
+ m_State = State_Stopped;
+ return NOERROR;
+ }
+
+ CBaseFilter::Stop();
+
+ // If we are going into a stopped state then we must decommit whatever
+ // allocator we are using it so that any source filter waiting in the
+ // GetBuffer can be released and unlock themselves for a state change
+
+ if (m_pInputPin->Allocator()) {
+ m_pInputPin->Allocator()->Decommit();
+ }
+
+ // Cancel any scheduled rendering
+
+ SetRepaintStatus(TRUE);
+ StopStreaming();
+ SourceThreadCanWait(FALSE);
+ ResetEndOfStream();
+ CancelNotification();
+
+ // There should be no outstanding clock advise
+ ASSERT(CancelNotification() == S_FALSE);
+ ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
+ ASSERT(m_EndOfStreamTimer == 0);
+
+ Ready();
+ WaitForReceiveToComplete();
+ m_bAbort = FALSE;
+
+ return NOERROR;
+}
+
+
+// When we pause the filter the things we do are:-
+
+// Commit the allocator being used in the connection
+// Allow a source filter thread to wait in Receive
+// Cancel any clock advise link (we may be running)
+// Possibly complete the state change if we have data
+// Allow us to be paused when we are not connected
+
+STDMETHODIMP CBaseRenderer::Pause()
+{
+ CAutoLock cRendererLock(&m_InterfaceLock);
+ FILTER_STATE OldState = m_State;
+ ASSERT(m_pInputPin->IsFlushing() == FALSE);
+
+ // Make sure there really is a state change
+
+ if (m_State == State_Paused) {
+ return CompleteStateChange(State_Paused);
+ }
+
+ // Has our input pin been connected
+
+ if (m_pInputPin->IsConnected() == FALSE) {
+ NOTE("Input pin is not connected");
+ m_State = State_Paused;
+ return CompleteStateChange(State_Paused);
+ }
+
+ // Pause the base filter class
+
+ HRESULT hr = CBaseFilter::Pause();
+ if (FAILED(hr)) {
+ NOTE("Pause failed");
+ return hr;
+ }
+
+ // Enable EC_REPAINT events again
+
+ SetRepaintStatus(TRUE);
+ StopStreaming();
+ SourceThreadCanWait(TRUE);
+ CancelNotification();
+ ResetEndOfStreamTimer();
+
+ // If we are going into a paused state then we must commit whatever
+ // allocator we are using it so that any source filter can call the
+ // GetBuffer and expect to get a buffer without returning an error
+
+ if (m_pInputPin->Allocator()) {
+ m_pInputPin->Allocator()->Commit();
+ }
+
+ // There should be no outstanding advise
+ ASSERT(CancelNotification() == S_FALSE);
+ ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
+ ASSERT(m_EndOfStreamTimer == 0);
+ ASSERT(m_pInputPin->IsFlushing() == FALSE);
+
+ // When we come out of a stopped state we must clear any image we were
+ // holding onto for frame refreshing. Since renderers see state changes
+ // first we can reset ourselves ready to accept the source thread data
+ // Paused or running after being stopped causes the current position to
+ // be reset so we're not interested in passing end of stream signals
+
+ if (OldState == State_Stopped) {
+ m_bAbort = FALSE;
+ ClearPendingSample();
+ }
+ return CompleteStateChange(OldState);
+}
+
+
+// When we run the filter the things we do are:-
+
+// Commit the allocator being used in the connection
+// Allow a source filter thread to wait in Receive
+// Signal the render event just to get us going
+// Start the base class by calling StartStreaming
+// Allow us to be run when we are not connected
+// Signal EC_COMPLETE if we are not connected
+
+STDMETHODIMP CBaseRenderer::Run(REFERENCE_TIME StartTime)
+{
+ CAutoLock cRendererLock(&m_InterfaceLock);
+ FILTER_STATE OldState = m_State;
+
+ // Make sure there really is a state change
+
+ if (m_State == State_Running) {
+ return NOERROR;
+ }
+
+ // Send EC_COMPLETE if we're not connected
+
+ if (m_pInputPin->IsConnected() == FALSE) {
+ NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);
+ m_State = State_Running;
+ return NOERROR;
+ }
+
+ Ready();
+
+ // Pause the base filter class
+
+ HRESULT hr = CBaseFilter::Run(StartTime);
+ if (FAILED(hr)) {
+ NOTE("Run failed");
+ return hr;
+ }
+
+ // Allow the source thread to wait
+ ASSERT(m_pInputPin->IsFlushing() == FALSE);
+ SourceThreadCanWait(TRUE);
+ SetRepaintStatus(FALSE);
+
+ // There should be no outstanding advise
+ ASSERT(CancelNotification() == S_FALSE);
+ ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
+ ASSERT(m_EndOfStreamTimer == 0);
+ ASSERT(m_pInputPin->IsFlushing() == FALSE);
+
+ // If we are going into a running state then we must commit whatever
+ // allocator we are using it so that any source filter can call the
+ // GetBuffer and expect to get a buffer without returning an error
+
+ if (m_pInputPin->Allocator()) {
+ m_pInputPin->Allocator()->Commit();
+ }
+
+ // When we come out of a stopped state we must clear any image we were
+ // holding onto for frame refreshing. Since renderers see state changes
+ // first we can reset ourselves ready to accept the source thread data
+ // Paused or running after being stopped causes the current position to
+ // be reset so we're not interested in passing end of stream signals
+
+ if (OldState == State_Stopped) {
+ m_bAbort = FALSE;
+ ClearPendingSample();
+ }
+ return StartStreaming();
+}
+
+
+// Return the number of input pins we support
+
+int CBaseRenderer::GetPinCount()
+{
+ if (m_pInputPin == NULL) {
+ // Try to create it
+ (void)GetPin(0);
+ }
+ return m_pInputPin != NULL ? 1 : 0;
+}
+
+
+// We only support one input pin and it is numbered zero
+
+CBasePin *CBaseRenderer::GetPin(int n)
+{
+ CAutoLock cObjectCreationLock(&m_ObjectCreationLock);
+
+ // Should only ever be called with zero
+ ASSERT(n == 0);
+
+ if (n != 0) {
+ return NULL;
+ }
+
+ // Create the input pin if not already done so
+
+ if (m_pInputPin == NULL) {
+
+ // hr must be initialized to NOERROR because
+ // CRendererInputPin's constructor only changes
+ // hr's value if an error occurs.
+ HRESULT hr = NOERROR;
+
+ m_pInputPin = new CRendererInputPin(this,&hr,L"In");
+ if (NULL == m_pInputPin) {
+ return NULL;
+ }
+
+ if (FAILED(hr)) {
+ delete m_pInputPin;
+ m_pInputPin = NULL;
+ return NULL;
+ }
+ }
+ return m_pInputPin;
+}
+
+
+// If "In" then return the IPin for our input pin, otherwise NULL and error
+
+STDMETHODIMP CBaseRenderer::FindPin(LPCWSTR Id, __deref_out IPin **ppPin)
+{
+ CheckPointer(ppPin,E_POINTER);
+
+ if (0==lstrcmpW(Id,L"In")) {
+ *ppPin = GetPin(0);
+ if (*ppPin) {
+ (*ppPin)->AddRef();
+ } else {
+ return E_OUTOFMEMORY;
+ }
+ } else {
+ *ppPin = NULL;
+ return VFW_E_NOT_FOUND;
+ }
+ return NOERROR;
+}
+
+
+// Called when the input pin receives an EndOfStream notification. If we have
+// not got a sample, then notify EC_COMPLETE now. If we have samples, then set
+// m_bEOS and check for this on completing samples. If we're waiting to pause
+// then complete the transition to paused state by setting the state event
+
+HRESULT CBaseRenderer::EndOfStream()
+{
+ // Ignore these calls if we are stopped
+
+ if (m_State == State_Stopped) {
+ return NOERROR;
+ }
+
+ // If we have a sample then wait for it to be rendered
+
+ m_bEOS = TRUE;
+ if (m_pMediaSample) {
+ return NOERROR;
+ }
+
+ // If we are waiting for pause then we are now ready since we cannot now
+ // carry on waiting for a sample to arrive since we are being told there
+ // won't be any. This sets an event that the GetState function picks up
+
+ Ready();
+
+ // Only signal completion now if we are running otherwise queue it until
+ // we do run in StartStreaming. This is used when we seek because a seek
+ // causes a pause where early notification of completion is misleading
+
+ if (m_bStreaming) {
+ SendEndOfStream();
+ }
+ return NOERROR;
+}
+
+
+// When we are told to flush we should release the source thread
+
+HRESULT CBaseRenderer::BeginFlush()
+{
+ // If paused then report state intermediate until we get some data
+
+ if (m_State == State_Paused) {
+ NotReady();
+ }
+
+ SourceThreadCanWait(FALSE);
+ CancelNotification();
+ ClearPendingSample();
+ // Wait for Receive to complete
+ WaitForReceiveToComplete();
+
+ return NOERROR;
+}
+
+
+// After flushing the source thread can wait in Receive again
+
+HRESULT CBaseRenderer::EndFlush()
+{
+ // Reset the current sample media time
+ if (m_pPosition) m_pPosition->ResetMediaTime();
+
+ // There should be no outstanding advise
+
+ ASSERT(CancelNotification() == S_FALSE);
+ SourceThreadCanWait(TRUE);
+ return NOERROR;
+}
+
+
+// We can now send EC_REPAINTs if so required
+
+HRESULT CBaseRenderer::CompleteConnect(IPin *pReceivePin)
+{
+ // The caller should always hold the interface lock because
+ // the function uses CBaseFilter::m_State.
+ ASSERT(CritCheckIn(&m_InterfaceLock));
+
+ m_bAbort = FALSE;
+
+ if (State_Running == GetRealState()) {
+ HRESULT hr = StartStreaming();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ SetRepaintStatus(FALSE);
+ } else {
+ SetRepaintStatus(TRUE);
+ }
+
+ return NOERROR;
+}
+
+
+// Called when we go paused or running
+
+HRESULT CBaseRenderer::Active()
+{
+ return NOERROR;
+}
+
+
+// Called when we go into a stopped state
+
+HRESULT CBaseRenderer::Inactive()
+{
+ if (m_pPosition) {
+ m_pPosition->ResetMediaTime();
+ }
+ // People who derive from this may want to override this behaviour
+ // to keep hold of the sample in some circumstances
+ ClearPendingSample();
+
+ return NOERROR;
+}
+
+
+// Tell derived classes about the media type agreed
+
+HRESULT CBaseRenderer::SetMediaType(const CMediaType *pmt)
+{
+ return NOERROR;
+}
+
+
+// When we break the input pin connection we should reset the EOS flags. When
+// we are asked for either IMediaPosition or IMediaSeeking we will create a
+// CPosPassThru object to handles media time pass through. When we're handed
+// samples we store (by calling CPosPassThru::RegisterMediaTime) their media
+// times so we can then return a real current position of data being rendered
+
+HRESULT CBaseRenderer::BreakConnect()
+{
+ // Do we have a quality management sink
+
+ if (m_pQSink) {
+ m_pQSink->Release();
+ m_pQSink = NULL;
+ }
+
+ // Check we have a valid connection
+
+ if (m_pInputPin->IsConnected() == FALSE) {
+ return S_FALSE;
+ }
+
+ // Check we are stopped before disconnecting
+ if (m_State != State_Stopped && !m_pInputPin->CanReconnectWhenActive()) {
+ return VFW_E_NOT_STOPPED;
+ }
+
+ SetRepaintStatus(FALSE);
+ ResetEndOfStream();
+ ClearPendingSample();
+ m_bAbort = FALSE;
+
+ if (State_Running == m_State) {
+ StopStreaming();
+ }
+
+ return NOERROR;
+}
+
+
+// Retrieves the sample times for this samples (note the sample times are
+// passed in by reference not value). We return S_FALSE to say schedule this
+// sample according to the times on the sample. We also return S_OK in
+// which case the object should simply render the sample data immediately
+
+HRESULT CBaseRenderer::GetSampleTimes(IMediaSample *pMediaSample,
+ __out REFERENCE_TIME *pStartTime,
+ __out REFERENCE_TIME *pEndTime)
+{
+ ASSERT(m_dwAdvise == 0);
+ ASSERT(pMediaSample);
+
+ // If the stop time for this sample is before or the same as start time,
+ // then just ignore it (release it) and schedule the next one in line
+ // Source filters should always fill in the start and end times properly!
+
+ if (SUCCEEDED(pMediaSample->GetTime(pStartTime, pEndTime))) {
+ if (*pEndTime < *pStartTime) {
+ return VFW_E_START_TIME_AFTER_END;
+ }
+ } else {
+ // no time set in the sample... draw it now?
+ return S_OK;
+ }
+
+ // Can't synchronise without a clock so we return S_OK which tells the
+ // caller that the sample should be rendered immediately without going
+ // through the overhead of setting a timer advise link with the clock
+
+ if (m_pClock == NULL) {
+ return S_OK;
+ }
+ return ShouldDrawSampleNow(pMediaSample,pStartTime,pEndTime);
+}
+
+
+// By default all samples are drawn according to their time stamps so we
+// return S_FALSE. Returning S_OK means draw immediately, this is used
+// by the derived video renderer class in its quality management.
+
+HRESULT CBaseRenderer::ShouldDrawSampleNow(IMediaSample *pMediaSample,
+ __out REFERENCE_TIME *ptrStart,
+ __out REFERENCE_TIME *ptrEnd)
+{
+ return S_FALSE;
+}
+
+
+// We must always reset the current advise time to zero after a timer fires
+// because there are several possible ways which lead us not to do any more
+// scheduling such as the pending image being cleared after state changes
+
+void CBaseRenderer::SignalTimerFired()
+{
+ m_dwAdvise = 0;
+}
+
+
+// Cancel any notification currently scheduled. This is called by the owning
+// window object when it is told to stop streaming. If there is no timer link
+// outstanding then calling this is benign otherwise we go ahead and cancel
+// We must always reset the render event as the quality management code can
+// signal immediate rendering by setting the event without setting an advise
+// link. If we're subsequently stopped and run the first attempt to setup an
+// advise link with the reference clock will find the event still signalled
+
+HRESULT CBaseRenderer::CancelNotification()
+{
+ ASSERT(m_dwAdvise == 0 || m_pClock);
+ DWORD_PTR dwAdvise = m_dwAdvise;
+
+ // Have we a live advise link
+
+ if (m_dwAdvise) {
+ m_pClock->Unadvise(m_dwAdvise);
+ SignalTimerFired();
+ ASSERT(m_dwAdvise == 0);
+ }
+
+ // Clear the event and return our status
+
+ m_RenderEvent.Reset();
+ return (dwAdvise ? S_OK : S_FALSE);
+}
+
+
+// Responsible for setting up one shot advise links with the clock
+// Return FALSE if the sample is to be dropped (not drawn at all)
+// Return TRUE if the sample is to be drawn and in this case also
+// arrange for m_RenderEvent to be set at the appropriate time
+
+BOOL CBaseRenderer::ScheduleSample(IMediaSample *pMediaSample)
+{
+ REFERENCE_TIME StartSample, EndSample;
+
+ // Is someone pulling our leg
+
+ if (pMediaSample == NULL) {
+ return FALSE;
+ }
+
+ // Get the next sample due up for rendering. If there aren't any ready
+ // then GetNextSampleTimes returns an error. If there is one to be done
+ // then it succeeds and yields the sample times. If it is due now then
+ // it returns S_OK other if it's to be done when due it returns S_FALSE
+
+ HRESULT hr = GetSampleTimes(pMediaSample, &StartSample, &EndSample);
+ if (FAILED(hr)) {
+ return FALSE;
+ }
+
+ // If we don't have a reference clock then we cannot set up the advise
+ // time so we simply set the event indicating an image to render. This
+ // will cause us to run flat out without any timing or synchronisation
+
+ if (hr == S_OK) {
+ EXECUTE_ASSERT(SetEvent((HANDLE) m_RenderEvent));
+ return TRUE;
+ }
+
+ ASSERT(m_dwAdvise == 0);
+ ASSERT(m_pClock);
+ ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
+
+ // We do have a valid reference clock interface so we can ask it to
+ // set an event when the image comes due for rendering. We pass in
+ // the reference time we were told to start at and also the current
+ // stream time which is the offset from the start reference time
+
+ hr = m_pClock->AdviseTime(
+ (REFERENCE_TIME) m_tStart, // Start run time
+ StartSample, // Stream time
+ (HEVENT)(HANDLE) m_RenderEvent, // Render notification
+ &m_dwAdvise); // Advise cookie
+
+ if (SUCCEEDED(hr)) {
+ return TRUE;
+ }
+
+ // We could not schedule the next sample for rendering despite the fact
+ // we have a valid sample here. This is a fair indication that either
+ // the system clock is wrong or the time stamp for the sample is duff
+
+ ASSERT(m_dwAdvise == 0);
+ return FALSE;
+}
+
+
+// This is called when a sample comes due for rendering. We pass the sample
+// on to the derived class. After rendering we will initialise the timer for
+// the next sample, NOTE signal that the last one fired first, if we don't
+// do this it thinks there is still one outstanding that hasn't completed
+
+HRESULT CBaseRenderer::Render(IMediaSample *pMediaSample)
+{
+ // If the media sample is NULL then we will have been notified by the
+ // clock that another sample is ready but in the mean time someone has
+ // stopped us streaming which causes the next sample to be released
+
+ if (pMediaSample == NULL) {
+ return S_FALSE;
+ }
+
+ // If we have stopped streaming then don't render any more samples, the
+ // thread that got in and locked us and then reset this flag does not
+ // clear the pending sample as we can use it to refresh any output device
+
+ if (m_bStreaming == FALSE) {
+ return S_FALSE;
+ }
+
+ // Time how long the rendering takes
+
+ OnRenderStart(pMediaSample);
+ DoRenderSample(pMediaSample);
+ OnRenderEnd(pMediaSample);
+
+ return NOERROR;
+}
+
+
+// Checks if there is a sample waiting at the renderer
+
+BOOL CBaseRenderer::HaveCurrentSample()
+{
+ CAutoLock cRendererLock(&m_RendererLock);
+ return (m_pMediaSample == NULL ? FALSE : TRUE);
+}
+
+
+// Returns the current sample waiting at the video renderer. We AddRef the
+// sample before returning so that should it come due for rendering the
+// person who called this method will hold the remaining reference count
+// that will stop the sample being added back onto the allocator free list
+
+IMediaSample *CBaseRenderer::GetCurrentSample()
+{
+ CAutoLock cRendererLock(&m_RendererLock);
+ if (m_pMediaSample) {
+ m_pMediaSample->AddRef();
+ }
+ return m_pMediaSample;
+}
+
+
+// Called when the source delivers us a sample. We go through a few checks to
+// make sure the sample can be rendered. If we are running (streaming) then we
+// have the sample scheduled with the reference clock, if we are not streaming
+// then we have received an sample in paused mode so we can complete any state
+// transition. On leaving this function everything will be unlocked so an app
+// thread may get in and change our state to stopped (for example) in which
+// case it will also signal the thread event so that our wait call is stopped
+
+HRESULT CBaseRenderer::PrepareReceive(IMediaSample *pMediaSample)
+{
+ CAutoLock cInterfaceLock(&m_InterfaceLock);
+ m_bInReceive = TRUE;
+
+ // Check our flushing and filter state
+
+ // This function must hold the interface lock because it calls
+ // CBaseInputPin::Receive() and CBaseInputPin::Receive() uses
+ // CBasePin::m_bRunTimeError.
+ HRESULT hr = m_pInputPin->CBaseInputPin::Receive(pMediaSample);
+
+ if (hr != NOERROR) {
+ m_bInReceive = FALSE;
+ return E_FAIL;
+ }
+
+ // Has the type changed on a media sample. We do all rendering
+ // synchronously on the source thread, which has a side effect
+ // that only one buffer is ever outstanding. Therefore when we
+ // have Receive called we can go ahead and change the format
+ // Since the format change can cause a SendMessage we just don't
+ // lock
+ if (m_pInputPin->SampleProps()->pMediaType) {
+ hr = m_pInputPin->SetMediaType(
+ (CMediaType *)m_pInputPin->SampleProps()->pMediaType);
+ if (FAILED(hr)) {
+ m_bInReceive = FALSE;
+ return hr;
+ }
+ }
+
+
+ CAutoLock cSampleLock(&m_RendererLock);
+
+ ASSERT(IsActive() == TRUE);
+ ASSERT(m_pInputPin->IsFlushing() == FALSE);
+ ASSERT(m_pInputPin->IsConnected() == TRUE);
+ ASSERT(m_pMediaSample == NULL);
+
+ // Return an error if we already have a sample waiting for rendering
+ // source pins must serialise the Receive calls - we also check that
+ // no data is being sent after the source signalled an end of stream
+
+ if (m_pMediaSample || m_bEOS || m_bAbort) {
+ Ready();
+ m_bInReceive = FALSE;
+ return E_UNEXPECTED;
+ }
+
+ // Store the media times from this sample
+ if (m_pPosition) m_pPosition->RegisterMediaTime(pMediaSample);
+
+ // Schedule the next sample if we are streaming
+
+ if ((m_bStreaming == TRUE) && (ScheduleSample(pMediaSample) == FALSE)) {
+ ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
+ ASSERT(CancelNotification() == S_FALSE);
+ m_bInReceive = FALSE;
+ return VFW_E_SAMPLE_REJECTED;
+ }
+
+ // Store the sample end time for EC_COMPLETE handling
+ m_SignalTime = m_pInputPin->SampleProps()->tStop;
+
+ // BEWARE we sometimes keep the sample even after returning the thread to
+ // the source filter such as when we go into a stopped state (we keep it
+ // to refresh the device with) so we must AddRef it to keep it safely. If
+ // we start flushing the source thread is released and any sample waiting
+ // will be released otherwise GetBuffer may never return (see BeginFlush)
+
+ m_pMediaSample = pMediaSample;
+ m_pMediaSample->AddRef();
+
+ if (m_bStreaming == FALSE) {
+ SetRepaintStatus(TRUE);
+ }
+ return NOERROR;
+}
+
+
+// Called by the source filter when we have a sample to render. Under normal
+// circumstances we set an advise link with the clock, wait for the time to
+// arrive and then render the data using the PURE virtual DoRenderSample that
+// the derived class will have overriden. After rendering the sample we may
+// also signal EOS if it was the last one sent before EndOfStream was called
+
+HRESULT CBaseRenderer::Receive(IMediaSample *pSample)
+{
+ ASSERT(pSample);
+
+ // It may return VFW_E_SAMPLE_REJECTED code to say don't bother
+
+ HRESULT hr = PrepareReceive(pSample);
+ ASSERT(m_bInReceive == SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ if (hr == VFW_E_SAMPLE_REJECTED) {
+ return NOERROR;
+ }
+ return hr;
+ }
+
+ // We realize the palette in "PrepareRender()" so we have to give away the
+ // filter lock here.
+ if (m_State == State_Paused) {
+ PrepareRender();
+ // no need to use InterlockedExchange
+ m_bInReceive = FALSE;
+ {
+ // We must hold both these locks
+ CAutoLock cRendererLock(&m_InterfaceLock);
+ if (m_State == State_Stopped)
+ return NOERROR;
+
+ m_bInReceive = TRUE;
+ CAutoLock cSampleLock(&m_RendererLock);
+ OnReceiveFirstSample(pSample);
+ }
+ Ready();
+ }
+ // Having set an advise link with the clock we sit and wait. We may be
+ // awoken by the clock firing or by a state change. The rendering call
+ // will lock the critical section and check we can still render the data
+
+ hr = WaitForRenderTime();
+ if (FAILED(hr)) {
+ m_bInReceive = FALSE;
+ return NOERROR;
+ }
+
+ PrepareRender();
+
+ // Set this here and poll it until we work out the locking correctly
+ // It can't be right that the streaming stuff grabs the interface
+ // lock - after all we want to be able to wait for this stuff
+ // to complete
+ m_bInReceive = FALSE;
+
+ // We must hold both these locks
+ CAutoLock cRendererLock(&m_InterfaceLock);
+
+ // since we gave away the filter wide lock, the sate of the filter could
+ // have chnaged to Stopped
+ if (m_State == State_Stopped)
+ return NOERROR;
+
+ CAutoLock cSampleLock(&m_RendererLock);
+
+ // Deal with this sample
+
+ Render(m_pMediaSample);
+ ClearPendingSample();
+ SendEndOfStream();
+ CancelNotification();
+ return NOERROR;
+}
+
+
+// This is called when we stop or are inactivated to clear the pending sample
+// We release the media sample interface so that they can be allocated to the
+// source filter again, unless of course we are changing state to inactive in
+// which case GetBuffer will return an error. We must also reset the current
+// media sample to NULL so that we know we do not currently have an image
+
+HRESULT CBaseRenderer::ClearPendingSample()
+{
+ CAutoLock cRendererLock(&m_RendererLock);
+ if (m_pMediaSample) {
+ m_pMediaSample->Release();
+ m_pMediaSample = NULL;
+ }
+ return NOERROR;
+}
+
+
+// Used to signal end of stream according to the sample end time
+
+void CALLBACK EndOfStreamTimer(UINT uID, // Timer identifier
+ UINT uMsg, // Not currently used
+ DWORD_PTR dwUser,// User information
+ DWORD_PTR dw1, // Windows reserved
+ DWORD_PTR dw2) // is also reserved
+{
+ CBaseRenderer *pRenderer = (CBaseRenderer *) dwUser;
+ NOTE1("EndOfStreamTimer called (%d)",uID);
+ pRenderer->TimerCallback();
+}
+
+// Do the timer callback work
+void CBaseRenderer::TimerCallback()
+{
+ // Lock for synchronization (but don't hold this lock when calling
+ // timeKillEvent)
+ CAutoLock cRendererLock(&m_RendererLock);
+
+ // See if we should signal end of stream now
+
+ if (m_EndOfStreamTimer) {
+ m_EndOfStreamTimer = 0;
+ SendEndOfStream();
+ }
+}
+
+
+// If we are at the end of the stream signal the filter graph but do not set
+// the state flag back to FALSE. Once we drop off the end of the stream we
+// leave the flag set (until a subsequent ResetEndOfStream). Each sample we
+// get delivered will update m_SignalTime to be the last sample's end time.
+// We must wait this long before signalling end of stream to the filtergraph
+
+#define TIMEOUT_DELIVERYWAIT 50
+#define TIMEOUT_RESOLUTION 10
+
+HRESULT CBaseRenderer::SendEndOfStream()
+{
+ ASSERT(CritCheckIn(&m_RendererLock));
+ if (m_bEOS == FALSE || m_bEOSDelivered || m_EndOfStreamTimer) {
+ return NOERROR;
+ }
+
+ // If there is no clock then signal immediately
+ if (m_pClock == NULL) {
+ return NotifyEndOfStream();
+ }
+
+ // How long into the future is the delivery time
+
+ REFERENCE_TIME Signal = m_tStart + m_SignalTime;
+ REFERENCE_TIME CurrentTime;
+ m_pClock->GetTime(&CurrentTime);
+ LONG Delay = LONG((Signal - CurrentTime) / 10000);
+
+ // Dump the timing information to the debugger
+
+ NOTE1("Delay until end of stream delivery %d",Delay);
+ NOTE1("Current %s",(LPCTSTR)CDisp((LONGLONG)CurrentTime));
+ NOTE1("Signal %s",(LPCTSTR)CDisp((LONGLONG)Signal));
+
+ // Wait for the delivery time to arrive
+
+ if (Delay < TIMEOUT_DELIVERYWAIT) {
+ return NotifyEndOfStream();
+ }
+
+ // Signal a timer callback on another worker thread
+
+ m_EndOfStreamTimer = CompatibleTimeSetEvent((UINT) Delay, // Period of timer
+ TIMEOUT_RESOLUTION, // Timer resolution
+ EndOfStreamTimer, // Callback function
+ DWORD_PTR(this), // Used information
+ TIME_ONESHOT); // Type of callback
+ if (m_EndOfStreamTimer == 0) {
+ return NotifyEndOfStream();
+ }
+ return NOERROR;
+}
+
+
+// Signals EC_COMPLETE to the filtergraph manager
+
+HRESULT CBaseRenderer::NotifyEndOfStream()
+{
+ CAutoLock cRendererLock(&m_RendererLock);
+ ASSERT(m_bEOSDelivered == FALSE);
+ ASSERT(m_EndOfStreamTimer == 0);
+
+ // Has the filter changed state
+
+ if (m_bStreaming == FALSE) {
+ ASSERT(m_EndOfStreamTimer == 0);
+ return NOERROR;
+ }
+
+ // Reset the end of stream timer
+ m_EndOfStreamTimer = 0;
+
+ // If we've been using the IMediaPosition interface, set it's start
+ // and end media "times" to the stop position by hand. This ensures
+ // that we actually get to the end, even if the MPEG guestimate has
+ // been bad or if the quality management dropped the last few frames
+
+ if (m_pPosition) m_pPosition->EOS();
+ m_bEOSDelivered = TRUE;
+ NOTE("Sending EC_COMPLETE...");
+ return NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);
+}
+
+
+// Reset the end of stream flag, this is typically called when we transfer to
+// stopped states since that resets the current position back to the start so
+// we will receive more samples or another EndOfStream if there aren't any. We
+// keep two separate flags one to say we have run off the end of the stream
+// (this is the m_bEOS flag) and another to say we have delivered EC_COMPLETE
+// to the filter graph. We need the latter otherwise we can end up sending an
+// EC_COMPLETE every time the source changes state and calls our EndOfStream
+
+HRESULT CBaseRenderer::ResetEndOfStream()
+{
+ ResetEndOfStreamTimer();
+ CAutoLock cRendererLock(&m_RendererLock);
+
+ m_bEOS = FALSE;
+ m_bEOSDelivered = FALSE;
+ m_SignalTime = 0;
+
+ return NOERROR;
+}
+
+
+// Kills any outstanding end of stream timer
+
+void CBaseRenderer::ResetEndOfStreamTimer()
+{
+ ASSERT(CritCheckOut(&m_RendererLock));
+ if (m_EndOfStreamTimer) {
+ timeKillEvent(m_EndOfStreamTimer);
+ m_EndOfStreamTimer = 0;
+ }
+}
+
+
+// This is called when we start running so that we can schedule any pending
+// image we have with the clock and display any timing information. If we
+// don't have any sample but we have queued an EOS flag then we send it. If
+// we do have a sample then we wait until that has been rendered before we
+// signal the filter graph otherwise we may change state before it's done
+
+HRESULT CBaseRenderer::StartStreaming()
+{
+ CAutoLock cRendererLock(&m_RendererLock);
+ if (m_bStreaming == TRUE) {
+ return NOERROR;
+ }
+
+ // Reset the streaming times ready for running
+
+ m_bStreaming = TRUE;
+
+ timeBeginPeriod(1);
+ OnStartStreaming();
+
+ // There should be no outstanding advise
+ ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
+ ASSERT(CancelNotification() == S_FALSE);
+
+ // If we have an EOS and no data then deliver it now
+
+ if (m_pMediaSample == NULL) {
+ return SendEndOfStream();
+ }
+
+ // Have the data rendered
+
+ ASSERT(m_pMediaSample);
+ if (!ScheduleSample(m_pMediaSample))
+ m_RenderEvent.Set();
+
+ return NOERROR;
+}
+
+
+// This is called when we stop streaming so that we can set our internal flag
+// indicating we are not now to schedule any more samples arriving. The state
+// change methods in the filter implementation take care of cancelling any
+// clock advise link we have set up and clearing any pending sample we have
+
+HRESULT CBaseRenderer::StopStreaming()
+{
+ CAutoLock cRendererLock(&m_RendererLock);
+ m_bEOSDelivered = FALSE;
+
+ if (m_bStreaming == TRUE) {
+ m_bStreaming = FALSE;
+ OnStopStreaming();
+ timeEndPeriod(1);
+ }
+ return NOERROR;
+}
+
+
+// We have a boolean flag that is reset when we have signalled EC_REPAINT to
+// the filter graph. We set this when we receive an image so that should any
+// conditions arise again we can send another one. By having a flag we ensure
+// we don't flood the filter graph with redundant calls. We do not set the
+// event when we receive an EndOfStream call since there is no point in us
+// sending further EC_REPAINTs. In particular the AutoShowWindow method and
+// the DirectDraw object use this method to control the window repainting
+
+void CBaseRenderer::SetRepaintStatus(BOOL bRepaint)
+{
+ CAutoLock cSampleLock(&m_RendererLock);
+ m_bRepaintStatus = bRepaint;
+}
+
+
+// Pass the window handle to the upstream filter
+
+void CBaseRenderer::SendNotifyWindow(IPin *pPin,HWND hwnd)
+{
+ IMediaEventSink *pSink;
+
+ // Does the pin support IMediaEventSink
+ HRESULT hr = pPin->QueryInterface(IID_IMediaEventSink,(void **)&pSink);
+ if (SUCCEEDED(hr)) {
+ pSink->Notify(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);
+ pSink->Release();
+ }
+ NotifyEvent(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);
+}
+
+
+// Signal an EC_REPAINT to the filter graph. This can be used to have data
+// sent to us. For example when a video window is first displayed it may
+// not have an image to display, at which point it signals EC_REPAINT. The
+// filtergraph will either pause the graph if stopped or if already paused
+// it will call put_CurrentPosition of the current position. Setting the
+// current position to itself has the stream flushed and the image resent
+
+#define RLOG(_x_) DbgLog((LOG_TRACE,1,TEXT(_x_)));
+
+void CBaseRenderer::SendRepaint()
+{
+ CAutoLock cSampleLock(&m_RendererLock);
+ ASSERT(m_pInputPin);
+
+ // We should not send repaint notifications when...
+ // - An end of stream has been notified
+ // - Our input pin is being flushed
+ // - The input pin is not connected
+ // - We have aborted a video playback
+ // - There is a repaint already sent
+
+ if (m_bAbort == FALSE) {
+ if (m_pInputPin->IsConnected() == TRUE) {
+ if (m_pInputPin->IsFlushing() == FALSE) {
+ if (IsEndOfStream() == FALSE) {
+ if (m_bRepaintStatus == TRUE) {
+ IPin *pPin = (IPin *) m_pInputPin;
+ NotifyEvent(EC_REPAINT,(LONG_PTR) pPin,0);
+ SetRepaintStatus(FALSE);
+ RLOG("Sending repaint");
+ }
+ }
+ }
+ }
+ }
+}
+
+
+// When a video window detects a display change (WM_DISPLAYCHANGE message) it
+// can send an EC_DISPLAY_CHANGED event code along with the renderer pin. The
+// filtergraph will stop everyone and reconnect our input pin. As we're then
+// reconnected we can accept the media type that matches the new display mode
+// since we may no longer be able to draw the current image type efficiently
+
+BOOL CBaseRenderer::OnDisplayChange()
+{
+ // Ignore if we are not connected yet
+
+ CAutoLock cSampleLock(&m_RendererLock);
+ if (m_pInputPin->IsConnected() == FALSE) {
+ return FALSE;
+ }
+
+ RLOG("Notification of EC_DISPLAY_CHANGE");
+
+ // Pass our input pin as parameter on the event
+
+ IPin *pPin = (IPin *) m_pInputPin;
+ m_pInputPin->AddRef();
+ NotifyEvent(EC_DISPLAY_CHANGED,(LONG_PTR) pPin,0);
+ SetAbortSignal(TRUE);
+ ClearPendingSample();
+ m_pInputPin->Release();
+
+ return TRUE;
+}
+
+
+// Called just before we start drawing.
+// Store the current time in m_trRenderStart to allow the rendering time to be
+// logged. Log the time stamp of the sample and how late it is (neg is early)
+
+void CBaseRenderer::OnRenderStart(IMediaSample *pMediaSample)
+{
+#ifdef PERF
+ REFERENCE_TIME trStart, trEnd;
+ pMediaSample->GetTime(&trStart, &trEnd);
+
+ MSR_INTEGER(m_idBaseStamp, (int)trStart); // dump low order 32 bits
+
+ m_pClock->GetTime(&m_trRenderStart);
+ MSR_INTEGER(0, (int)m_trRenderStart);
+ REFERENCE_TIME trStream;
+ trStream = m_trRenderStart-m_tStart; // convert reftime to stream time
+ MSR_INTEGER(0,(int)trStream);
+
+ const int trLate = (int)(trStream - trStart);
+ MSR_INTEGER(m_idBaseAccuracy, trLate/10000); // dump in mSec
+#endif
+
+} // OnRenderStart
+
+
+// Called directly after drawing an image.
+// calculate the time spent drawing and log it.
+
+void CBaseRenderer::OnRenderEnd(IMediaSample *pMediaSample)
+{
+#ifdef PERF
+ REFERENCE_TIME trNow;
+ m_pClock->GetTime(&trNow);
+ MSR_INTEGER(0,(int)trNow);
+ int t = (int)((trNow - m_trRenderStart)/10000); // convert UNITS->msec
+ MSR_INTEGER(m_idBaseRenderTime, t);
+#endif
+} // OnRenderEnd
+
+
+
+
+// Constructor must be passed the base renderer object
+
+CRendererInputPin::CRendererInputPin(__inout CBaseRenderer *pRenderer,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR pPinName) :
+ CBaseInputPin(NAME("Renderer pin"),
+ pRenderer,
+ &pRenderer->m_InterfaceLock,
+ (HRESULT *) phr,
+ pPinName)
+{
+ m_pRenderer = pRenderer;
+ ASSERT(m_pRenderer);
+}
+
+
+// Signals end of data stream on the input pin
+
+STDMETHODIMP CRendererInputPin::EndOfStream()
+{
+ CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
+ CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);
+
+ // Make sure we're streaming ok
+
+ HRESULT hr = CheckStreaming();
+ if (hr != NOERROR) {
+ return hr;
+ }
+
+ // Pass it onto the renderer
+
+ hr = m_pRenderer->EndOfStream();
+ if (SUCCEEDED(hr)) {
+ hr = CBaseInputPin::EndOfStream();
+ }
+ return hr;
+}
+
+
+// Signals start of flushing on the input pin - we do the final reset end of
+// stream with the renderer lock unlocked but with the interface lock locked
+// We must do this because we call timeKillEvent, our timer callback method
+// has to take the renderer lock to serialise our state. Therefore holding a
+// renderer lock when calling timeKillEvent could cause a deadlock condition
+
+STDMETHODIMP CRendererInputPin::BeginFlush()
+{
+ CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
+ {
+ CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);
+ CBaseInputPin::BeginFlush();
+ m_pRenderer->BeginFlush();
+ }
+ return m_pRenderer->ResetEndOfStream();
+}
+
+
+// Signals end of flushing on the input pin
+
+STDMETHODIMP CRendererInputPin::EndFlush()
+{
+ CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
+ CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);
+
+ HRESULT hr = m_pRenderer->EndFlush();
+ if (SUCCEEDED(hr)) {
+ hr = CBaseInputPin::EndFlush();
+ }
+ return hr;
+}
+
+
+// Pass the sample straight through to the renderer object
+
+STDMETHODIMP CRendererInputPin::Receive(IMediaSample *pSample)
+{
+ HRESULT hr = m_pRenderer->Receive(pSample);
+ if (FAILED(hr)) {
+
+ // A deadlock could occur if the caller holds the renderer lock and
+ // attempts to acquire the interface lock.
+ ASSERT(CritCheckOut(&m_pRenderer->m_RendererLock));
+
+ {
+ // The interface lock must be held when the filter is calling
+ // IsStopped() or IsFlushing(). The interface lock must also
+ // be held because the function uses m_bRunTimeError.
+ CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
+
+ // We do not report errors which occur while the filter is stopping,
+ // flushing or if the m_bAbort flag is set . Errors are expected to
+ // occur during these operations and the streaming thread correctly
+ // handles the errors.
+ if (!IsStopped() && !IsFlushing() && !m_pRenderer->m_bAbort && !m_bRunTimeError) {
+
+ // EC_ERRORABORT's first parameter is the error which caused
+ // the event and its' last parameter is 0. See the Direct
+ // Show SDK documentation for more information.
+ m_pRenderer->NotifyEvent(EC_ERRORABORT,hr,0);
+
+ {
+ CAutoLock alRendererLock(&m_pRenderer->m_RendererLock);
+ if (m_pRenderer->IsStreaming() && !m_pRenderer->IsEndOfStreamDelivered()) {
+ m_pRenderer->NotifyEndOfStream();
+ }
+ }
+
+ m_bRunTimeError = TRUE;
+ }
+ }
+ }
+
+ return hr;
+}
+
+
+// Called when the input pin is disconnected
+
+HRESULT CRendererInputPin::BreakConnect()
+{
+ HRESULT hr = m_pRenderer->BreakConnect();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return CBaseInputPin::BreakConnect();
+}
+
+
+// Called when the input pin is connected
+
+HRESULT CRendererInputPin::CompleteConnect(IPin *pReceivePin)
+{
+ HRESULT hr = m_pRenderer->CompleteConnect(pReceivePin);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return CBaseInputPin::CompleteConnect(pReceivePin);
+}
+
+
+// Give the pin id of our one and only pin
+
+STDMETHODIMP CRendererInputPin::QueryId(__deref_out LPWSTR *Id)
+{
+ CheckPointer(Id,E_POINTER);
+
+ const WCHAR szIn[] = L"In";
+
+ *Id = (LPWSTR)CoTaskMemAlloc(sizeof(szIn));
+ if (*Id == NULL) {
+ return E_OUTOFMEMORY;
+ }
+ CopyMemory(*Id, szIn, sizeof(szIn));
+ return NOERROR;
+}
+
+
+// Will the filter accept this media type
+
+HRESULT CRendererInputPin::CheckMediaType(const CMediaType *pmt)
+{
+ return m_pRenderer->CheckMediaType(pmt);
+}
+
+
+// Called when we go paused or running
+
+HRESULT CRendererInputPin::Active()
+{
+ return m_pRenderer->Active();
+}
+
+
+// Called when we go into a stopped state
+
+HRESULT CRendererInputPin::Inactive()
+{
+ // The caller must hold the interface lock because
+ // this function uses m_bRunTimeError.
+ ASSERT(CritCheckIn(&m_pRenderer->m_InterfaceLock));
+
+ m_bRunTimeError = FALSE;
+
+ return m_pRenderer->Inactive();
+}
+
+
+// Tell derived classes about the media type agreed
+
+HRESULT CRendererInputPin::SetMediaType(const CMediaType *pmt)
+{
+ HRESULT hr = CBaseInputPin::SetMediaType(pmt);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return m_pRenderer->SetMediaType(pmt);
+}
+
+
+// We do not keep an event object to use when setting up a timer link with
+// the clock but are given a pointer to one by the owning object through the
+// SetNotificationObject method - this must be initialised before starting
+// We can override the default quality management process to have it always
+// draw late frames, this is currently done by having the following registry
+// key (actually an INI key) called DrawLateFrames set to 1 (default is 0)
+
+const TCHAR AMQUALITY[] = TEXT("ActiveMovie");
+const TCHAR DRAWLATEFRAMES[] = TEXT("DrawLateFrames");
+
+CBaseVideoRenderer::CBaseVideoRenderer(
+ REFCLSID RenderClass, // CLSID for this renderer
+ __in_opt LPCTSTR pName, // Debug ONLY description
+ __inout_opt LPUNKNOWN pUnk, // Aggregated owner object
+ __inout HRESULT *phr) : // General OLE return code
+
+ CBaseRenderer(RenderClass,pName,pUnk,phr),
+ m_cFramesDropped(0),
+ m_cFramesDrawn(0),
+ m_bSupplierHandlingQuality(FALSE)
+{
+ ResetStreamingTimes();
+
+#ifdef PERF
+ m_idTimeStamp = MSR_REGISTER(TEXT("Frame time stamp"));
+ m_idEarliness = MSR_REGISTER(TEXT("Earliness fudge"));
+ m_idTarget = MSR_REGISTER(TEXT("Target (mSec)"));
+ m_idSchLateTime = MSR_REGISTER(TEXT("mSec late when scheduled"));
+ m_idDecision = MSR_REGISTER(TEXT("Scheduler decision code"));
+ m_idQualityRate = MSR_REGISTER(TEXT("Quality rate sent"));
+ m_idQualityTime = MSR_REGISTER(TEXT("Quality time sent"));
+ m_idWaitReal = MSR_REGISTER(TEXT("Render wait"));
+ // m_idWait = MSR_REGISTER(TEXT("wait time recorded (msec)"));
+ m_idFrameAccuracy = MSR_REGISTER(TEXT("Frame accuracy (msecs)"));
+ m_bDrawLateFrames = GetProfileInt(AMQUALITY, DRAWLATEFRAMES, FALSE);
+ //m_idSendQuality = MSR_REGISTER(TEXT("Processing Quality message"));
+
+ m_idRenderAvg = MSR_REGISTER(TEXT("Render draw time Avg"));
+ m_idFrameAvg = MSR_REGISTER(TEXT("FrameAvg"));
+ m_idWaitAvg = MSR_REGISTER(TEXT("WaitAvg"));
+ m_idDuration = MSR_REGISTER(TEXT("Duration"));
+ m_idThrottle = MSR_REGISTER(TEXT("Audio-video throttle wait"));
+ // m_idDebug = MSR_REGISTER(TEXT("Debug stuff"));
+#endif // PERF
+} // Constructor
+
+
+// Destructor is just a placeholder
+
+CBaseVideoRenderer::~CBaseVideoRenderer()
+{
+ ASSERT(m_dwAdvise == 0);
+}
+
+
+// The timing functions in this class are called by the window object and by
+// the renderer's allocator.
+// The windows object calls timing functions as it receives media sample
+// images for drawing using GDI.
+// The allocator calls timing functions when it starts passing DCI/DirectDraw
+// surfaces which are not rendered in the same way; The decompressor writes
+// directly to the surface with no separate rendering, so those code paths
+// call direct into us. Since we only ever hand out DCI/DirectDraw surfaces
+// when we have allocated one and only one image we know there cannot be any
+// conflict between the two.
+//
+// We use timeGetTime to return the timing counts we use (since it's relative
+// performance we are interested in rather than absolute compared to a clock)
+// The window object sets the accuracy of the system clock (normally 1ms) by
+// calling timeBeginPeriod/timeEndPeriod when it changes streaming states
+
+
+// Reset all times controlling streaming.
+// Set them so that
+// 1. Frames will not initially be dropped
+// 2. The first frame will definitely be drawn (achieved by saying that there
+// has not ben a frame drawn for a long time).
+
+HRESULT CBaseVideoRenderer::ResetStreamingTimes()
+{
+ m_trLastDraw = -1000; // set up as first frame since ages (1 sec) ago
+ m_tStreamingStart = timeGetTime();
+ m_trRenderAvg = 0;
+ m_trFrameAvg = -1; // -1000 fps == "unset"
+ m_trDuration = 0; // 0 - strange value
+ m_trRenderLast = 0;
+ m_trWaitAvg = 0;
+ m_tRenderStart = 0;
+ m_cFramesDrawn = 0;
+ m_cFramesDropped = 0;
+ m_iTotAcc = 0;
+ m_iSumSqAcc = 0;
+ m_iSumSqFrameTime = 0;
+ m_trFrame = 0; // hygeine - not really needed
+ m_trLate = 0; // hygeine - not really needed
+ m_iSumFrameTime = 0;
+ m_nNormal = 0;
+ m_trEarliness = 0;
+ m_trTarget = -300000; // 30mSec early
+ m_trThrottle = 0;
+ m_trRememberStampForPerf = 0;
+
+#ifdef PERF
+ m_trRememberFrameForPerf = 0;
+#endif
+
+ return NOERROR;
+} // ResetStreamingTimes
+
+
+// Reset all times controlling streaming. Note that we're now streaming. We
+// don't need to set the rendering event to have the source filter released
+// as it is done during the Run processing. When we are run we immediately
+// release the source filter thread and draw any image waiting (that image
+// may already have been drawn once as a poster frame while we were paused)
+
+HRESULT CBaseVideoRenderer::OnStartStreaming()
+{
+ ResetStreamingTimes();
+ return NOERROR;
+} // OnStartStreaming
+
+
+// Called at end of streaming. Fixes times for property page report
+
+HRESULT CBaseVideoRenderer::OnStopStreaming()
+{
+ m_tStreamingStart = timeGetTime()-m_tStreamingStart;
+ return NOERROR;
+} // OnStopStreaming
+
+
+// Called when we start waiting for a rendering event.
+// Used to update times spent waiting and not waiting.
+
+void CBaseVideoRenderer::OnWaitStart()
+{
+ MSR_START(m_idWaitReal);
+} // OnWaitStart
+
+
+// Called when we are awoken from the wait in the window OR by our allocator
+// when it is hanging around until the next sample is due for rendering on a
+// DCI/DirectDraw surface. We add the wait time into our rolling average.
+// We grab the interface lock so that we're serialised with the application
+// thread going through the run code - which in due course ends up calling
+// ResetStreaming times - possibly as we run through this section of code
+
+void CBaseVideoRenderer::OnWaitEnd()
+{
+#ifdef PERF
+ MSR_STOP(m_idWaitReal);
+ // for a perf build we want to know just exactly how late we REALLY are.
+ // even if this means that we have to look at the clock again.
+
+ REFERENCE_TIME trRealStream; // the real time now expressed as stream time.
+#if 0
+ m_pClock->GetTime(&trRealStream); // Calling clock here causes W95 deadlock!
+#else
+ // We will be discarding overflows like mad here!
+ // This is wrong really because timeGetTime() can wrap but it's
+ // only for PERF
+ REFERENCE_TIME tr = timeGetTime()*10000;
+ trRealStream = tr + m_llTimeOffset;
+#endif
+ trRealStream -= m_tStart; // convert to stream time (this is a reftime)
+
+ if (m_trRememberStampForPerf==0) {
+ // This is probably the poster frame at the start, and it is not scheduled
+ // in the usual way at all. Just count it. The rememberstamp gets set
+ // in ShouldDrawSampleNow, so this does invalid frame recording until we
+ // actually start playing.
+ PreparePerformanceData(0, 0);
+ } else {
+ int trLate = (int)(trRealStream - m_trRememberStampForPerf);
+ int trFrame = (int)(tr - m_trRememberFrameForPerf);
+ PreparePerformanceData(trLate, trFrame);
+ }
+ m_trRememberFrameForPerf = tr;
+#endif //PERF
+} // OnWaitEnd
+
+
+// Put data on one side that describes the lateness of the current frame.
+// We don't yet know whether it will actually be drawn. In direct draw mode,
+// this decision is up to the filter upstream, and it could change its mind.
+// The rules say that if it did draw it must call Receive(). One way or
+// another we eventually get into either OnRenderStart or OnDirectRender and
+// these both call RecordFrameLateness to update the statistics.
+
+void CBaseVideoRenderer::PreparePerformanceData(int trLate, int trFrame)
+{
+ m_trLate = trLate;
+ m_trFrame = trFrame;
+} // PreparePerformanceData
+
+
+// update the statistics:
+// m_iTotAcc, m_iSumSqAcc, m_iSumSqFrameTime, m_iSumFrameTime, m_cFramesDrawn
+// Note that because the properties page reports using these variables,
+// 1. We need to be inside a critical section
+// 2. They must all be updated together. Updating the sums here and the count
+// elsewhere can result in imaginary jitter (i.e. attempts to find square roots
+// of negative numbers) in the property page code.
+
+void CBaseVideoRenderer::RecordFrameLateness(int trLate, int trFrame)
+{
+ // Record how timely we are.
+ int tLate = trLate/10000;
+
+ // Best estimate of moment of appearing on the screen is average of
+ // start and end draw times. Here we have only the end time. This may
+ // tend to show us as spuriously late by up to 1/2 frame rate achieved.
+ // Decoder probably monitors draw time. We don't bother.
+ MSR_INTEGER( m_idFrameAccuracy, tLate );
+
+ // This is a kludge - we can get frames that are very late
+ // especially (at start-up) and they invalidate the statistics.
+ // So ignore things that are more than 1 sec off.
+ if (tLate>1000 || tLate<-1000) {
+ if (m_cFramesDrawn<=1) {
+ tLate = 0;
+ } else if (tLate>0) {
+ tLate = 1000;
+ } else {
+ tLate = -1000;
+ }
+ }
+ // The very first frame often has a invalid time, so don't
+ // count it into the statistics. (???)
+ if (m_cFramesDrawn>1) {
+ m_iTotAcc += tLate;
+ m_iSumSqAcc += (tLate*tLate);
+ }
+
+ // calculate inter-frame time. Doesn't make sense for first frame
+ // second frame suffers from invalid first frame stamp.
+ if (m_cFramesDrawn>2) {
+ int tFrame = trFrame/10000; // convert to mSec else it overflows
+
+ // This is a kludge. It can overflow anyway (a pause can cause
+ // a very long inter-frame time) and it overflows at 2**31/10**7
+ // or about 215 seconds i.e. 3min 35sec
+ if (tFrame>1000||tFrame<0) tFrame = 1000;
+ m_iSumSqFrameTime += tFrame*tFrame;
+ ASSERT(m_iSumSqFrameTime>=0);
+ m_iSumFrameTime += tFrame;
+ }
+ ++m_cFramesDrawn;
+
+} // RecordFrameLateness
+
+
+void CBaseVideoRenderer::ThrottleWait()
+{
+ if (m_trThrottle>0) {
+ int iThrottle = m_trThrottle/10000; // convert to mSec
+ MSR_INTEGER( m_idThrottle, iThrottle);
+ DbgLog((LOG_TRACE, 0, TEXT("Throttle %d ms"), iThrottle));
+ Sleep(iThrottle);
+ } else {
+ Sleep(0);
+ }
+} // ThrottleWait
+
+
+// Whenever a frame is rendered it goes though either OnRenderStart
+// or OnDirectRender. Data that are generated during ShouldDrawSample
+// are added to the statistics by calling RecordFrameLateness from both
+// these two places.
+
+// Called in place of OnRenderStart..OnRenderEnd
+// When a DirectDraw image is drawn
+void CBaseVideoRenderer::OnDirectRender(IMediaSample *pMediaSample)
+{
+ m_trRenderAvg = 0;
+ m_trRenderLast = 5000000; // If we mode switch, we do NOT want this
+ // to inhibit the new average getting going!
+ // so we set it to half a second
+ // MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);
+ RecordFrameLateness(m_trLate, m_trFrame);
+ ThrottleWait();
+} // OnDirectRender
+
+
+// Called just before we start drawing. All we do is to get the current clock
+// time (from the system) and return. We have to store the start render time
+// in a member variable because it isn't used until we complete the drawing
+// The rest is just performance logging.
+
+void CBaseVideoRenderer::OnRenderStart(IMediaSample *pMediaSample)
+{
+ RecordFrameLateness(m_trLate, m_trFrame);
+ m_tRenderStart = timeGetTime();
+} // OnRenderStart
+
+
+// Called directly after drawing an image. We calculate the time spent in the
+// drawing code and if this doesn't appear to have any odd looking spikes in
+// it then we add it to the current average draw time. Measurement spikes may
+// occur if the drawing thread is interrupted and switched to somewhere else.
+
+void CBaseVideoRenderer::OnRenderEnd(IMediaSample *pMediaSample)
+{
+ // The renderer time can vary erratically if we are interrupted so we do
+ // some smoothing to help get more sensible figures out but even that is
+ // not enough as figures can go 9,10,9,9,83,9 and we must disregard 83
+
+ int tr = (timeGetTime() - m_tRenderStart)*10000; // convert mSec->UNITS
+ if (tr < m_trRenderAvg*2 || tr < 2 * m_trRenderLast) {
+ // DO_MOVING_AVG(m_trRenderAvg, tr);
+ m_trRenderAvg = (tr + (AVGPERIOD-1)*m_trRenderAvg)/AVGPERIOD;
+ }
+ m_trRenderLast = tr;
+ ThrottleWait();
+} // OnRenderEnd
+
+
+STDMETHODIMP CBaseVideoRenderer::SetSink( IQualityControl * piqc)
+{
+
+ m_pQSink = piqc;
+
+ return NOERROR;
+} // SetSink
+
+
+STDMETHODIMP CBaseVideoRenderer::Notify( IBaseFilter * pSelf, Quality q)
+{
+ // NOTE: We are NOT getting any locks here. We could be called
+ // asynchronously and possibly even on a time critical thread of
+ // someone else's - so we do the minumum. We only set one state
+ // variable (an integer) and if that happens to be in the middle
+ // of another thread reading it they will just get either the new
+ // or the old value. Locking would achieve no more than this.
+
+ // It might be nice to check that we are being called from m_pGraph, but
+ // it turns out to be a millisecond or so per throw!
+
+ // This is heuristics, these numbers are aimed at being "what works"
+ // rather than anything based on some theory.
+ // We use a hyperbola because it's easy to calculate and it includes
+ // a panic button asymptote (which we push off just to the left)
+ // The throttling fits the following table (roughly)
+ // Proportion Throttle (msec)
+ // >=1000 0
+ // 900 3
+ // 800 7
+ // 700 11
+ // 600 17
+ // 500 25
+ // 400 35
+ // 300 50
+ // 200 72
+ // 125 100
+ // 100 112
+ // 50 146
+ // 0 200
+
+ // (some evidence that we could go for a sharper kink - e.g. no throttling
+ // until below the 750 mark - might give fractionally more frames on a
+ // P60-ish machine). The easy way to get these coefficients is to use
+ // Renbase.xls follow the instructions therein using excel solver.
+
+ if (q.Proportion>=1000) { m_trThrottle = 0; }
+ else {
+ // The DWORD is to make quite sure I get unsigned arithmetic
+ // as the constant is between 2**31 and 2**32
+ m_trThrottle = -330000 + (388880000/(q.Proportion+167));
+ }
+ return NOERROR;
+} // Notify
+
+
+// Send a message to indicate what our supplier should do about quality.
+// Theory:
+// What a supplier wants to know is "is the frame I'm working on NOW
+// going to be late?".
+// F1 is the frame at the supplier (as above)
+// Tf1 is the due time for F1
+// T1 is the time at that point (NOW!)
+// Tr1 is the time that f1 WILL actually be rendered
+// L1 is the latency of the graph for frame F1 = Tr1-T1
+// D1 (for delay) is how late F1 will be beyond its due time i.e.
+// D1 = (Tr1-Tf1) which is what the supplier really wants to know.
+// Unfortunately Tr1 is in the future and is unknown, so is L1
+//
+// We could estimate L1 by its value for a previous frame,
+// L0 = Tr0-T0 and work off
+// D1' = ((T1+L0)-Tf1) = (T1 + (Tr0-T0) -Tf1)
+// Rearranging terms:
+// D1' = (T1-T0) + (Tr0-Tf1)
+// adding (Tf0-Tf0) and rearranging again:
+// = (T1-T0) + (Tr0-Tf0) + (Tf0-Tf1)
+// = (T1-T0) - (Tf1-Tf0) + (Tr0-Tf0)
+// But (Tr0-Tf0) is just D0 - how late frame zero was, and this is the
+// Late field in the quality message that we send.
+// The other two terms just state what correction should be applied before
+// using the lateness of F0 to predict the lateness of F1.
+// (T1-T0) says how much time has actually passed (we have lost this much)
+// (Tf1-Tf0) says how much time should have passed if we were keeping pace
+// (we have gained this much).
+//
+// Suppliers should therefore work off:
+// Quality.Late + (T1-T0) - (Tf1-Tf0)
+// and see if this is "acceptably late" or even early (i.e. negative).
+// They get T1 and T0 by polling the clock, they get Tf1 and Tf0 from
+// the time stamps in the frames. They get Quality.Late from us.
+//
+
+HRESULT CBaseVideoRenderer::SendQuality(REFERENCE_TIME trLate,
+ REFERENCE_TIME trRealStream)
+{
+ Quality q;
+ HRESULT hr;
+
+ // If we are the main user of time, then report this as Flood/Dry.
+ // If our suppliers are, then report it as Famine/Glut.
+ //
+ // We need to take action, but avoid hunting. Hunting is caused by
+ // 1. Taking too much action too soon and overshooting
+ // 2. Taking too long to react (so averaging can CAUSE hunting).
+ //
+ // The reason why we use trLate as well as Wait is to reduce hunting;
+ // if the wait time is coming down and about to go into the red, we do
+ // NOT want to rely on some average which is only telling is that it used
+ // to be OK once.
+
+ q.TimeStamp = (REFERENCE_TIME)trRealStream;
+
+ if (m_trFrameAvg<0) {
+ q.Type = Famine; // guess
+ }
+ // Is the greater part of the time taken bltting or something else
+ else if (m_trFrameAvg > 2*m_trRenderAvg) {
+ q.Type = Famine; // mainly other
+ } else {
+ q.Type = Flood; // mainly bltting
+ }
+
+ q.Proportion = 1000; // default
+
+ if (m_trFrameAvg<0) {
+ // leave it alone - we don't know enough
+ }
+ else if ( trLate> 0 ) {
+ // try to catch up over the next second
+ // We could be Really, REALLY late, but rendering all the frames
+ // anyway, just because it's so cheap.
+
+ q.Proportion = 1000 - (int)((trLate)/(UNITS/1000));
+ if (q.Proportion<500) {
+ q.Proportion = 500; // don't go daft. (could've been negative!)
+ } else {
+ }
+
+ } else if ( m_trWaitAvg>20000
+ && trLate<-20000
+ ){
+ // Go cautiously faster - aim at 2mSec wait.
+ if (m_trWaitAvg>=m_trFrameAvg) {
+ // This can happen because of some fudges.
+ // The waitAvg is how long we originally planned to wait
+ // The frameAvg is more honest.
+ // It means that we are spending a LOT of time waiting
+ q.Proportion = 2000; // double.
+ } else {
+ if (m_trFrameAvg+20000 > m_trWaitAvg) {
+ q.Proportion
+ = 1000 * (m_trFrameAvg / (m_trFrameAvg + 20000 - m_trWaitAvg));
+ } else {
+ // We're apparently spending more than the whole frame time waiting.
+ // Assume that the averages are slightly out of kilter, but that we
+ // are indeed doing a lot of waiting. (This leg probably never
+ // happens, but the code avoids any potential divide by zero).
+ q.Proportion = 2000;
+ }
+ }
+
+ if (q.Proportion>2000) {
+ q.Proportion = 2000; // don't go crazy.
+ }
+ }
+
+ // Tell the supplier how late frames are when they get rendered
+ // That's how late we are now.
+ // If we are in directdraw mode then the guy upstream can see the drawing
+ // times and we'll just report on the start time. He can figure out any
+ // offset to apply. If we are in DIB Section mode then we will apply an
+ // extra offset which is half of our drawing time. This is usually small
+ // but can sometimes be the dominant effect. For this we will use the
+ // average drawing time rather than the last frame. If the last frame took
+ // a long time to draw and made us late, that's already in the lateness
+ // figure. We should not add it in again unless we expect the next frame
+ // to be the same. We don't, we expect the average to be a better shot.
+ // In direct draw mode the RenderAvg will be zero.
+
+ q.Late = trLate + m_trRenderAvg/2;
+
+ // log what we're doing
+ MSR_INTEGER(m_idQualityRate, q.Proportion);
+ MSR_INTEGER( m_idQualityTime, (int)q.Late / 10000 );
+
+ // A specific sink interface may be set through IPin
+
+ if (m_pQSink==NULL) {
+ // Get our input pin's peer. We send quality management messages
+ // to any nominated receiver of these things (set in the IPin
+ // interface), or else to our source filter.
+
+ IQualityControl *pQC = NULL;
+ IPin *pOutputPin = m_pInputPin->GetConnected();
+ ASSERT(pOutputPin != NULL);
+
+ // And get an AddRef'd quality control interface
+
+ hr = pOutputPin->QueryInterface(IID_IQualityControl,(void**) &pQC);
+ if (SUCCEEDED(hr)) {
+ m_pQSink = pQC;
+ }
+ }
+ if (m_pQSink) {
+ return m_pQSink->Notify(this,q);
+ }
+
+ return S_FALSE;
+
+} // SendQuality
+
+
+// We are called with a valid IMediaSample image to decide whether this is to
+// be drawn or not. There must be a reference clock in operation.
+// Return S_OK if it is to be drawn Now (as soon as possible)
+// Return S_FALSE if it is to be drawn when it's due
+// Return an error if we want to drop it
+// m_nNormal=-1 indicates that we dropped the previous frame and so this
+// one should be drawn early. Respect it and update it.
+// Use current stream time plus a number of heuristics (detailed below)
+// to make the decision
+
+HRESULT CBaseVideoRenderer::ShouldDrawSampleNow(IMediaSample *pMediaSample,
+ __inout REFERENCE_TIME *ptrStart,
+ __inout REFERENCE_TIME *ptrEnd)
+{
+
+ // Don't call us unless there's a clock interface to synchronise with
+ ASSERT(m_pClock);
+
+ MSR_INTEGER(m_idTimeStamp, (int)((*ptrStart)>>32)); // high order 32 bits
+ MSR_INTEGER(m_idTimeStamp, (int)(*ptrStart)); // low order 32 bits
+
+ // We lose a bit of time depending on the monitor type waiting for the next
+ // screen refresh. On average this might be about 8mSec - so it will be
+ // later than we think when the picture appears. To compensate a bit
+ // we bias the media samples by -8mSec i.e. 80000 UNITs.
+ // We don't ever make a stream time negative (call it paranoia)
+ if (*ptrStart>=80000) {
+ *ptrStart -= 80000;
+ *ptrEnd -= 80000; // bias stop to to retain valid frame duration
+ }
+
+ // Cache the time stamp now. We will want to compare what we did with what
+ // we started with (after making the monitor allowance).
+ m_trRememberStampForPerf = *ptrStart;
+
+ // Get reference times (current and late)
+ REFERENCE_TIME trRealStream; // the real time now expressed as stream time.
+ m_pClock->GetTime(&trRealStream);
+#ifdef PERF
+ // While the reference clock is expensive:
+ // Remember the offset from timeGetTime and use that.
+ // This overflows all over the place, but when we subtract to get
+ // differences the overflows all cancel out.
+ m_llTimeOffset = trRealStream-timeGetTime()*10000;
+#endif
+ trRealStream -= m_tStart; // convert to stream time (this is a reftime)
+
+ // We have to wory about two versions of "lateness". The truth, which we
+ // try to work out here and the one measured against m_trTarget which
+ // includes long term feedback. We report statistics against the truth
+ // but for operational decisions we work to the target.
+ // We use TimeDiff to make sure we get an integer because we
+ // may actually be late (or more likely early if there is a big time
+ // gap) by a very long time.
+ const int trTrueLate = TimeDiff(trRealStream - *ptrStart);
+ const int trLate = trTrueLate;
+
+ MSR_INTEGER(m_idSchLateTime, trTrueLate/10000);
+
+ // Send quality control messages upstream, measured against target
+ HRESULT hr = SendQuality(trLate, trRealStream);
+ // Note: the filter upstream is allowed to this FAIL meaning "you do it".
+ m_bSupplierHandlingQuality = (hr==S_OK);
+
+ // Decision time! Do we drop, draw when ready or draw immediately?
+
+ const int trDuration = (int)(*ptrEnd - *ptrStart);
+ {
+ // We need to see if the frame rate of the file has just changed.
+ // This would make comparing our previous frame rate with the current
+ // frame rate inefficent. Hang on a moment though. I've seen files
+ // where the frames vary between 33 and 34 mSec so as to average
+ // 30fps. A minor variation like that won't hurt us.
+ int t = m_trDuration/32;
+ if ( trDuration > m_trDuration+t
+ || trDuration < m_trDuration-t
+ ) {
+ // There's a major variation. Reset the average frame rate to
+ // exactly the current rate to disable decision 9002 for this frame,
+ // and remember the new rate.
+ m_trFrameAvg = trDuration;
+ m_trDuration = trDuration;
+ }
+ }
+
+ MSR_INTEGER(m_idEarliness, m_trEarliness/10000);
+ MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);
+ MSR_INTEGER(m_idFrameAvg, m_trFrameAvg/10000);
+ MSR_INTEGER(m_idWaitAvg, m_trWaitAvg/10000);
+ MSR_INTEGER(m_idDuration, trDuration/10000);
+
+#ifdef PERF
+ if (S_OK==pMediaSample->IsDiscontinuity()) {
+ MSR_INTEGER(m_idDecision, 9000);
+ }
+#endif
+
+ // Control the graceful slide back from slow to fast machine mode.
+ // After a frame drop accept an early frame and set the earliness to here
+ // If this frame is already later than the earliness then slide it to here
+ // otherwise do the standard slide (reduce by about 12% per frame).
+ // Note: earliness is normally NEGATIVE
+ BOOL bJustDroppedFrame
+ = ( m_bSupplierHandlingQuality
+ // Can't use the pin sample properties because we might
+ // not be in Receive when we call this
+ && (S_OK == pMediaSample->IsDiscontinuity()) // he just dropped one
+ )
+ || (m_nNormal==-1); // we just dropped one
+
+
+ // Set m_trEarliness (slide back from slow to fast machine mode)
+ if (trLate>0) {
+ m_trEarliness = 0; // we are no longer in fast machine mode at all!
+ } else if ( (trLate>=m_trEarliness) || bJustDroppedFrame) {
+ m_trEarliness = trLate; // Things have slipped of their own accord
+ } else {
+ m_trEarliness = m_trEarliness - m_trEarliness/8; // graceful slide
+ }
+
+ // prepare the new wait average - but don't pollute the old one until
+ // we have finished with it.
+ int trWaitAvg;
+ {
+ // We never mix in a negative wait. This causes us to believe in fast machines
+ // slightly more.
+ int trL = trLate<0 ? -trLate : 0;
+ trWaitAvg = (trL + m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;
+ }
+
+
+ int trFrame;
+ {
+ REFERENCE_TIME tr = trRealStream - m_trLastDraw; // Cd be large - 4 min pause!
+ if (tr>10000000) {
+ tr = 10000000; // 1 second - arbitrarily.
+ }
+ trFrame = int(tr);
+ }
+
+ // We will DRAW this frame IF...
+ if (
+ // ...the time we are spending drawing is a small fraction of the total
+ // observed inter-frame time so that dropping it won't help much.
+ (3*m_trRenderAvg <= m_trFrameAvg)
+
+ // ...or our supplier is NOT handling things and the next frame would
+ // be less timely than this one or our supplier CLAIMS to be handling
+ // things, and is now less than a full FOUR frames late.
+ || ( m_bSupplierHandlingQuality
+ ? (trLate <= trDuration*4)
+ : (trLate+trLate < trDuration)
+ )
+
+ // ...or we are on average waiting for over eight milliseconds then
+ // this may be just a glitch. Draw it and we'll hope to catch up.
+ || (m_trWaitAvg > 80000)
+
+ // ...or we haven't drawn an image for over a second. We will update
+ // the display, which stops the video looking hung.
+ // Do this regardless of how late this media sample is.
+ || ((trRealStream - m_trLastDraw) > UNITS)
+
+ ) {
+ HRESULT Result;
+
+ // We are going to play this frame. We may want to play it early.
+ // We will play it early if we think we are in slow machine mode.
+ // If we think we are NOT in slow machine mode, we will still play
+ // it early by m_trEarliness as this controls the graceful slide back.
+ // and in addition we aim at being m_trTarget late rather than "on time".
+
+ BOOL bPlayASAP = FALSE;
+
+ // we will play it AT ONCE (slow machine mode) if...
+
+ // ...we are playing catch-up
+ if ( bJustDroppedFrame) {
+ bPlayASAP = TRUE;
+ MSR_INTEGER(m_idDecision, 9001);
+ }
+
+ // ...or if we are running below the true frame rate
+ // exact comparisons are glitchy, for these measurements,
+ // so add an extra 5% or so
+ else if ( (m_trFrameAvg > trDuration + trDuration/16)
+
+ // It's possible to get into a state where we are losing ground, but
+ // are a very long way ahead. To avoid this or recover from it
+ // we refuse to play early by more than 10 frames.
+ && (trLate > - trDuration*10)
+ ){
+ bPlayASAP = TRUE;
+ MSR_INTEGER(m_idDecision, 9002);
+ }
+#if 0
+ // ...or if we have been late and are less than one frame early
+ else if ( (trLate + trDuration > 0)
+ && (m_trWaitAvg<=20000)
+ ) {
+ bPlayASAP = TRUE;
+ MSR_INTEGER(m_idDecision, 9003);
+ }
+#endif
+ // We will NOT play it at once if we are grossly early. On very slow frame
+ // rate movies - e.g. clock.avi - it is not a good idea to leap ahead just
+ // because we got starved (for instance by the net) and dropped one frame
+ // some time or other. If we are more than 900mSec early, then wait.
+ if (trLate<-9000000) {
+ bPlayASAP = FALSE;
+ }
+
+ if (bPlayASAP) {
+
+ m_nNormal = 0;
+ MSR_INTEGER(m_idDecision, 0);
+ // When we are here, we are in slow-machine mode. trLate may well
+ // oscillate between negative and positive when the supplier is
+ // dropping frames to keep sync. We should not let that mislead
+ // us into thinking that we have as much as zero spare time!
+ // We just update with a zero wait.
+ m_trWaitAvg = (m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;
+
+ // Assume that we draw it immediately. Update inter-frame stats
+ m_trFrameAvg = (trFrame + m_trFrameAvg*(AVGPERIOD-1))/AVGPERIOD;
+#ifndef PERF
+ // If this is NOT a perf build, then report what we know so far
+ // without looking at the clock any more. This assumes that we
+ // actually wait for exactly the time we hope to. It also reports
+ // how close we get to the manipulated time stamps that we now have
+ // rather than the ones we originally started with. It will
+ // therefore be a little optimistic. However it's fast.
+ PreparePerformanceData(trTrueLate, trFrame);
+#endif
+ m_trLastDraw = trRealStream;
+ if (m_trEarliness > trLate) {
+ m_trEarliness = trLate; // if we are actually early, this is neg
+ }
+ Result = S_OK; // Draw it now
+
+ } else {
+ ++m_nNormal;
+ // Set the average frame rate to EXACTLY the ideal rate.
+ // If we are exiting slow-machine mode then we will have caught up
+ // and be running ahead, so as we slide back to exact timing we will
+ // have a longer than usual gap at this point. If we record this
+ // real gap then we'll think that we're running slow and go back
+ // into slow-machine mode and vever get it straight.
+ m_trFrameAvg = trDuration;
+ MSR_INTEGER(m_idDecision, 1);
+
+ // Play it early by m_trEarliness and by m_trTarget
+
+ {
+ int trE = m_trEarliness;
+ if (trE < -m_trFrameAvg) {
+ trE = -m_trFrameAvg;
+ }
+ *ptrStart += trE; // N.B. earliness is negative
+ }
+
+ int Delay = -trTrueLate;
+ Result = Delay<=0 ? S_OK : S_FALSE; // OK = draw now, FALSE = wait
+
+ m_trWaitAvg = trWaitAvg;
+
+ // Predict when it will actually be drawn and update frame stats
+
+ if (Result==S_FALSE) { // We are going to wait
+ trFrame = TimeDiff(*ptrStart-m_trLastDraw);
+ m_trLastDraw = *ptrStart;
+ } else {
+ // trFrame is already = trRealStream-m_trLastDraw;
+ m_trLastDraw = trRealStream;
+ }
+#ifndef PERF
+ int iAccuracy;
+ if (Delay>0) {
+ // Report lateness based on when we intend to play it
+ iAccuracy = TimeDiff(*ptrStart-m_trRememberStampForPerf);
+ } else {
+ // Report lateness based on playing it *now*.
+ iAccuracy = trTrueLate; // trRealStream-RememberStampForPerf;
+ }
+ PreparePerformanceData(iAccuracy, trFrame);
+#endif
+ }
+ return Result;
+ }
+
+ // We are going to drop this frame!
+ // Of course in DirectDraw mode the guy upstream may draw it anyway.
+
+ // This will probably give a large negative wack to the wait avg.
+ m_trWaitAvg = trWaitAvg;
+
+#ifdef PERF
+ // Respect registry setting - debug only!
+ if (m_bDrawLateFrames) {
+ return S_OK; // draw it when it's ready
+ } // even though it's late.
+#endif
+
+ // We are going to drop this frame so draw the next one early
+ // n.b. if the supplier is doing direct draw then he may draw it anyway
+ // but he's doing something funny to arrive here in that case.
+
+ MSR_INTEGER(m_idDecision, 2);
+ m_nNormal = -1;
+ return E_FAIL; // drop it
+
+} // ShouldDrawSampleNow
+
+
+// NOTE we're called by both the window thread and the source filter thread
+// so we have to be protected by a critical section (locked before called)
+// Also, when the window thread gets signalled to render an image, it always
+// does so regardless of how late it is. All the degradation is done when we
+// are scheduling the next sample to be drawn. Hence when we start an advise
+// link to draw a sample, that sample's time will always become the last one
+// drawn - unless of course we stop streaming in which case we cancel links
+
+BOOL CBaseVideoRenderer::ScheduleSample(IMediaSample *pMediaSample)
+{
+ // We override ShouldDrawSampleNow to add quality management
+
+ BOOL bDrawImage = CBaseRenderer::ScheduleSample(pMediaSample);
+ if (bDrawImage == FALSE) {
+ ++m_cFramesDropped;
+ return FALSE;
+ }
+
+ // m_cFramesDrawn must NOT be updated here. It has to be updated
+ // in RecordFrameLateness at the same time as the other statistics.
+ return TRUE;
+}
+
+
+// Implementation of IQualProp interface needed to support the property page
+// This is how the property page gets the data out of the scheduler. We are
+// passed into the constructor the owning object in the COM sense, this will
+// either be the video renderer or an external IUnknown if we're aggregated.
+// We initialise our CUnknown base class with this interface pointer. Then
+// all we have to do is to override NonDelegatingQueryInterface to expose
+// our IQualProp interface. The AddRef and Release are handled automatically
+// by the base class and will be passed on to the appropriate outer object
+
+STDMETHODIMP CBaseVideoRenderer::get_FramesDroppedInRenderer(__out int *pcFramesDropped)
+{
+ CheckPointer(pcFramesDropped,E_POINTER);
+ CAutoLock cVideoLock(&m_InterfaceLock);
+ *pcFramesDropped = m_cFramesDropped;
+ return NOERROR;
+} // get_FramesDroppedInRenderer
+
+
+// Set *pcFramesDrawn to the number of frames drawn since
+// streaming started.
+
+STDMETHODIMP CBaseVideoRenderer::get_FramesDrawn( int *pcFramesDrawn)
+{
+ CheckPointer(pcFramesDrawn,E_POINTER);
+ CAutoLock cVideoLock(&m_InterfaceLock);
+ *pcFramesDrawn = m_cFramesDrawn;
+ return NOERROR;
+} // get_FramesDrawn
+
+
+// Set iAvgFrameRate to the frames per hundred secs since
+// streaming started. 0 otherwise.
+
+STDMETHODIMP CBaseVideoRenderer::get_AvgFrameRate( int *piAvgFrameRate)
+{
+ CheckPointer(piAvgFrameRate,E_POINTER);
+ CAutoLock cVideoLock(&m_InterfaceLock);
+
+ int t;
+ if (m_bStreaming) {
+ t = timeGetTime()-m_tStreamingStart;
+ } else {
+ t = m_tStreamingStart;
+ }
+
+ if (t<=0) {
+ *piAvgFrameRate = 0;
+ ASSERT(m_cFramesDrawn == 0);
+ } else {
+ // i is frames per hundred seconds
+ *piAvgFrameRate = MulDiv(100000, m_cFramesDrawn, t);
+ }
+ return NOERROR;
+} // get_AvgFrameRate
+
+
+// Set *piAvg to the average sync offset since streaming started
+// in mSec. The sync offset is the time in mSec between when the frame
+// should have been drawn and when the frame was actually drawn.
+
+STDMETHODIMP CBaseVideoRenderer::get_AvgSyncOffset(__out int *piAvg)
+{
+ CheckPointer(piAvg,E_POINTER);
+ CAutoLock cVideoLock(&m_InterfaceLock);
+
+ if (NULL==m_pClock) {
+ *piAvg = 0;
+ return NOERROR;
+ }
+
+ // Note that we didn't gather the stats on the first frame
+ // so we use m_cFramesDrawn-1 here
+ if (m_cFramesDrawn<=1) {
+ *piAvg = 0;
+ } else {
+ *piAvg = (int)(m_iTotAcc / (m_cFramesDrawn-1));
+ }
+ return NOERROR;
+} // get_AvgSyncOffset
+
+
+// To avoid dragging in the maths library - a cheap
+// approximate integer square root.
+// We do this by getting a starting guess which is between 1
+// and 2 times too large, followed by THREE iterations of
+// Newton Raphson. (That will give accuracy to the nearest mSec
+// for the range in question - roughly 0..1000)
+//
+// It would be faster to use a linear interpolation and ONE NR, but
+// who cares. If anyone does - the best linear interpolation is
+// to approximates sqrt(x) by
+// y = x * (sqrt(2)-1) + 1 - 1/sqrt(2) + 1/(8*(sqrt(2)-1))
+// 0r y = x*0.41421 + 0.59467
+// This minimises the maximal error in the range in question.
+// (error is about +0.008883 and then one NR will give error .0000something
+// (Of course these are integers, so you can't just multiply by 0.41421
+// you'd have to do some sort of MulDiv).
+// Anyone wanna check my maths? (This is only for a property display!)
+
+int isqrt(int x)
+{
+ int s = 1;
+ // Make s an initial guess for sqrt(x)
+ if (x > 0x40000000) {
+ s = 0x8000; // prevent any conceivable closed loop
+ } else {
+ while (s*s=0) s = (s*s+x)/(2*s);
+ if (s>=0) s = (s*s+x)/(2*s);
+ }
+ }
+ return s;
+}
+
+//
+// Do estimates for standard deviations for per-frame
+// statistics
+//
+HRESULT CBaseVideoRenderer::GetStdDev(
+ int nSamples,
+ __out int *piResult,
+ LONGLONG llSumSq,
+ LONGLONG iTot
+)
+{
+ CheckPointer(piResult,E_POINTER);
+ CAutoLock cVideoLock(&m_InterfaceLock);
+
+ if (NULL==m_pClock) {
+ *piResult = 0;
+ return NOERROR;
+ }
+
+ // If S is the Sum of the Squares of observations and
+ // T the Total (i.e. sum) of the observations and there were
+ // N observations, then an estimate of the standard deviation is
+ // sqrt( (S - T**2/N) / (N-1) )
+
+ if (nSamples<=1) {
+ *piResult = 0;
+ } else {
+ LONGLONG x;
+ // First frames have invalid stamps, so we get no stats for them
+ // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1
+
+ // so we use m_cFramesDrawn-1 here
+ x = llSumSq - llMulDiv(iTot, iTot, nSamples, 0);
+ x = x / (nSamples-1);
+ ASSERT(x>=0);
+ *piResult = isqrt((LONG)x);
+ }
+ return NOERROR;
+}
+
+// Set *piDev to the standard deviation in mSec of the sync offset
+// of each frame since streaming started.
+
+STDMETHODIMP CBaseVideoRenderer::get_DevSyncOffset(__out int *piDev)
+{
+ // First frames have invalid stamps, so we get no stats for them
+ // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1
+ return GetStdDev(m_cFramesDrawn - 1,
+ piDev,
+ m_iSumSqAcc,
+ m_iTotAcc);
+} // get_DevSyncOffset
+
+
+// Set *piJitter to the standard deviation in mSec of the inter-frame time
+// of frames since streaming started.
+
+STDMETHODIMP CBaseVideoRenderer::get_Jitter(__out int *piJitter)
+{
+ // First frames have invalid stamps, so we get no stats for them
+ // So second frame gives invalid inter-frame time
+ // So we need 3 frames to get 1 datum, so N is cFramesDrawn-2
+ return GetStdDev(m_cFramesDrawn - 2,
+ piJitter,
+ m_iSumSqFrameTime,
+ m_iSumFrameTime);
+} // get_Jitter
+
+
+// Overidden to return our IQualProp interface
+
+STDMETHODIMP
+CBaseVideoRenderer::NonDelegatingQueryInterface(REFIID riid,__deref_out VOID **ppv)
+{
+ // We return IQualProp and delegate everything else
+
+ if (riid == IID_IQualProp) {
+ return GetInterface( (IQualProp *)this, ppv);
+ } else if (riid == IID_IQualityControl) {
+ return GetInterface( (IQualityControl *)this, ppv);
+ }
+ return CBaseRenderer::NonDelegatingQueryInterface(riid,ppv);
+}
+
+
+// Override JoinFilterGraph so that, just before leaving
+// the graph we can send an EC_WINDOW_DESTROYED event
+
+STDMETHODIMP
+CBaseVideoRenderer::JoinFilterGraph(__inout_opt IFilterGraph *pGraph, __in_opt LPCWSTR pName)
+{
+ // Since we send EC_ACTIVATE, we also need to ensure
+ // we send EC_WINDOW_DESTROYED or the resource manager may be
+ // holding us as a focus object
+ if (!pGraph && m_pGraph) {
+
+ // We were in a graph and now we're not
+ // Do this properly in case we are aggregated
+ IBaseFilter* pFilter = this;
+ NotifyEvent(EC_WINDOW_DESTROYED, (LPARAM) pFilter, 0);
+ }
+ return CBaseFilter::JoinFilterGraph(pGraph, pName);
+}
+
+
+// This removes a large number of level 4 warnings from the
+// Microsoft compiler which in this case are not very useful
+#pragma warning(disable: 4514)
+
diff --git a/dshow_base/renbase.h b/dshow_base/renbase.h
index 5352e87..c2685bb 100644
--- a/dshow_base/renbase.h
+++ b/dshow_base/renbase.h
@@ -1,478 +1,478 @@
-//------------------------------------------------------------------------------
-// File: RenBase.h
-//
-// Desc: DirectShow base classes - defines a generic ActiveX base renderer
-// class.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __RENBASE__
-#define __RENBASE__
-
-// Forward class declarations
-
-class CBaseRenderer;
-class CBaseVideoRenderer;
-class CRendererInputPin;
-
-// This is our input pin class that channels calls to the renderer
-
-class CRendererInputPin : public CBaseInputPin
-{
-protected:
-
- CBaseRenderer *m_pRenderer;
-
-public:
-
- CRendererInputPin(CBaseRenderer *pRenderer,
- HRESULT *phr,
- LPCWSTR Name);
-
- // Overriden from the base pin classes
-
- HRESULT BreakConnect();
- HRESULT CompleteConnect(IPin *pReceivePin);
- HRESULT SetMediaType(const CMediaType *pmt);
- HRESULT CheckMediaType(const CMediaType *pmt);
- HRESULT Active();
- HRESULT Inactive();
-
- // Add rendering behaviour to interface functions
-
- STDMETHODIMP QueryId(LPWSTR *Id);
- STDMETHODIMP EndOfStream();
- STDMETHODIMP BeginFlush();
- STDMETHODIMP EndFlush();
- STDMETHODIMP Receive(IMediaSample *pMediaSample);
-
- // Helper
- IMemAllocator inline *Allocator() const
- {
- return m_pAllocator;
- }
-};
-
-// Main renderer class that handles synchronisation and state changes
-
-class CBaseRenderer : public CBaseFilter
-{
-protected:
-
- friend class CRendererInputPin;
-
- friend void CALLBACK EndOfStreamTimer(UINT uID, // Timer identifier
- UINT uMsg, // Not currently used
- DWORD_PTR dwUser, // User information
- DWORD_PTR dw1, // Windows reserved
- DWORD_PTR dw2); // Is also reserved
-
- CRendererPosPassThru *m_pPosition; // Media seeking pass by object
- CAMEvent m_RenderEvent; // Used to signal timer events
- CAMEvent m_ThreadSignal; // Signalled to release worker thread
- CAMEvent m_evComplete; // Signalled when state complete
- BOOL m_bAbort; // Stop us from rendering more data
- BOOL m_bStreaming; // Are we currently streaming
- DWORD_PTR m_dwAdvise; // Timer advise cookie
- IMediaSample *m_pMediaSample; // Current image media sample
- BOOL m_bEOS; // Any more samples in the stream
- BOOL m_bEOSDelivered; // Have we delivered an EC_COMPLETE
- CRendererInputPin *m_pInputPin; // Our renderer input pin object
- CCritSec m_InterfaceLock; // Critical section for interfaces
- CCritSec m_RendererLock; // Controls access to internals
- IQualityControl * m_pQSink; // QualityControl sink
- BOOL m_bRepaintStatus; // Can we signal an EC_REPAINT
- // Avoid some deadlocks by tracking filter during stop
- volatile BOOL m_bInReceive; // Inside Receive between PrepareReceive
- // And actually processing the sample
- REFERENCE_TIME m_SignalTime; // Time when we signal EC_COMPLETE
- UINT m_EndOfStreamTimer; // Used to signal end of stream
- CCritSec m_ObjectCreationLock; // This lock protects the creation and
- // of m_pPosition and m_pInputPin. It
- // ensures that two threads cannot create
- // either object simultaneously.
-
-public:
-
- CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer
- TCHAR *pName, // Debug ONLY description
- LPUNKNOWN pUnk, // Aggregated owner object
- HRESULT *phr); // General OLE return code
-
- ~CBaseRenderer();
-
- // Overriden to say what interfaces we support and where
-
- virtual HRESULT GetMediaPositionInterface(REFIID riid,void **ppv);
- STDMETHODIMP NonDelegatingQueryInterface(REFIID, void **);
-
- virtual HRESULT SourceThreadCanWait(BOOL bCanWait);
-
-#ifdef DEBUG
- // Debug only dump of the renderer state
- void DisplayRendererState();
-#endif
- virtual HRESULT WaitForRenderTime();
- virtual HRESULT CompleteStateChange(FILTER_STATE OldState);
-
- // Return internal information about this filter
-
- BOOL IsEndOfStream() { return m_bEOS; };
- BOOL IsEndOfStreamDelivered() { return m_bEOSDelivered; };
- BOOL IsStreaming() { return m_bStreaming; };
- void SetAbortSignal(BOOL bAbort) { m_bAbort = bAbort; };
- virtual void OnReceiveFirstSample(IMediaSample *pMediaSample) { };
- CAMEvent *GetRenderEvent() { return &m_RenderEvent; };
-
- // Permit access to the transition state
-
- void Ready() { m_evComplete.Set(); };
- void NotReady() { m_evComplete.Reset(); };
- BOOL CheckReady() { return m_evComplete.Check(); };
-
- virtual int GetPinCount();
- virtual CBasePin *GetPin(int n);
- FILTER_STATE GetRealState();
- void SendRepaint();
- void SendNotifyWindow(IPin *pPin,HWND hwnd);
- BOOL OnDisplayChange();
- void SetRepaintStatus(BOOL bRepaint);
-
- // Override the filter and pin interface functions
-
- STDMETHODIMP Stop();
- STDMETHODIMP Pause();
- STDMETHODIMP Run(REFERENCE_TIME StartTime);
- STDMETHODIMP GetState(DWORD dwMSecs,FILTER_STATE *State);
- STDMETHODIMP FindPin(LPCWSTR Id, IPin **ppPin);
-
- // These are available for a quality management implementation
-
- virtual void OnRenderStart(IMediaSample *pMediaSample);
- virtual void OnRenderEnd(IMediaSample *pMediaSample);
- virtual HRESULT OnStartStreaming() { return NOERROR; };
- virtual HRESULT OnStopStreaming() { return NOERROR; };
- virtual void OnWaitStart() { };
- virtual void OnWaitEnd() { };
- virtual void PrepareRender() { };
-
-#ifdef PERF
- REFERENCE_TIME m_trRenderStart; // Just before we started drawing
- // Set in OnRenderStart, Used in OnRenderEnd
- int m_idBaseStamp; // MSR_id for frame time stamp
- int m_idBaseRenderTime; // MSR_id for true wait time
- int m_idBaseAccuracy; // MSR_id for time frame is late (int)
-#endif
-
- // Quality management implementation for scheduling rendering
-
- virtual BOOL ScheduleSample(IMediaSample *pMediaSample);
- virtual HRESULT GetSampleTimes(IMediaSample *pMediaSample,
- REFERENCE_TIME *pStartTime,
- REFERENCE_TIME *pEndTime);
-
- virtual HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,
- REFERENCE_TIME *ptrStart,
- REFERENCE_TIME *ptrEnd);
-
- // Lots of end of stream complexities
-
- void TimerCallback();
- void ResetEndOfStreamTimer();
- HRESULT NotifyEndOfStream();
- virtual HRESULT SendEndOfStream();
- virtual HRESULT ResetEndOfStream();
- virtual HRESULT EndOfStream();
-
- // Rendering is based around the clock
-
- void SignalTimerFired();
- virtual HRESULT CancelNotification();
- virtual HRESULT ClearPendingSample();
-
- // Called when the filter changes state
-
- virtual HRESULT Active();
- virtual HRESULT Inactive();
- virtual HRESULT StartStreaming();
- virtual HRESULT StopStreaming();
- virtual HRESULT BeginFlush();
- virtual HRESULT EndFlush();
-
- // Deal with connections and type changes
-
- virtual HRESULT BreakConnect();
- virtual HRESULT SetMediaType(const CMediaType *pmt);
- virtual HRESULT CompleteConnect(IPin *pReceivePin);
-
- // These look after the handling of data samples
-
- virtual HRESULT PrepareReceive(IMediaSample *pMediaSample);
- virtual HRESULT Receive(IMediaSample *pMediaSample);
- virtual BOOL HaveCurrentSample();
- virtual IMediaSample *GetCurrentSample();
- virtual HRESULT Render(IMediaSample *pMediaSample);
-
- // Derived classes MUST override these
- virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE;
- virtual HRESULT CheckMediaType(const CMediaType *) PURE;
-
- // Helper
- void WaitForReceiveToComplete();
-};
-
-
-// CBaseVideoRenderer is a renderer class (see its ancestor class) and
-// it handles scheduling of media samples so that they are drawn at the
-// correct time by the reference clock. It implements a degradation
-// strategy. Possible degradation modes are:
-// Drop frames here (only useful if the drawing takes significant time)
-// Signal supplier (upstream) to drop some frame(s) - i.e. one-off skip.
-// Signal supplier to change the frame rate - i.e. ongoing skipping.
-// Or any combination of the above.
-// In order to determine what's useful to try we need to know what's going
-// on. This is done by timing various operations (including the supplier).
-// This timing is done by using timeGetTime as it is accurate enough and
-// usually cheaper than calling the reference clock. It also tells the
-// truth if there is an audio break and the reference clock stops.
-// We provide a number of public entry points (named OnXxxStart, OnXxxEnd)
-// which the rest of the renderer calls at significant moments. These do
-// the timing.
-
-// the number of frames that the sliding averages are averaged over.
-// the rule is (1024*NewObservation + (AVGPERIOD-1) * PreviousAverage)/AVGPERIOD
-#define AVGPERIOD 4
-#define DO_MOVING_AVG(avg,obs) (avg = (1024*obs + (AVGPERIOD-1)*avg)/AVGPERIOD)
-// Spot the bug in this macro - I can't. but it doesn't work!
-
-class CBaseVideoRenderer : public CBaseRenderer, // Base renderer class
- public IQualProp, // Property page guff
- public IQualityControl // Allow throttling
-{
-protected:
-
- // Hungarian:
- // tFoo is the time Foo in mSec (beware m_tStart from filter.h)
- // trBar is the time Bar by the reference clock
-
- //******************************************************************
- // State variables to control synchronisation
- //******************************************************************
-
- // Control of sending Quality messages. We need to know whether
- // we are in trouble (e.g. frames being dropped) and where the time
- // is being spent.
-
- // When we drop a frame we play the next one early.
- // The frame after that is likely to wait before drawing and counting this
- // wait as spare time is unfair, so we count it as a zero wait.
- // We therefore need to know whether we are playing frames early or not.
-
- int m_nNormal; // The number of consecutive frames
- // drawn at their normal time (not early)
- // -1 means we just dropped a frame.
-
-#ifdef PERF
- BOOL m_bDrawLateFrames; // Don't drop any frames (debug and I'm
- // not keen on people using it!)
-#endif
-
- BOOL m_bSupplierHandlingQuality;// The response to Quality messages says
- // our supplier is handling things.
- // We will allow things to go extra late
- // before dropping frames. We will play
- // very early after he has dropped one.
-
- // Control of scheduling, frame dropping etc.
- // We need to know where the time is being spent so as to tell whether
- // we should be taking action here, signalling supplier or what.
- // The variables are initialised to a mode of NOT dropping frames.
- // They will tell the truth after a few frames.
- // We typically record a start time for an event, later we get the time
- // again and subtract to get the elapsed time, and we average this over
- // a few frames. The average is used to tell what mode we are in.
-
- // Although these are reference times (64 bit) they are all DIFFERENCES
- // between times which are small. An int will go up to 214 secs before
- // overflow. Avoiding 64 bit multiplications and divisions seems
- // worth while.
-
-
-
- // Audio-video throttling. If the user has turned up audio quality
- // very high (in principle it could be any other stream, not just audio)
- // then we can receive cries for help via the graph manager. In this case
- // we put in a wait for some time after rendering each frame.
- int m_trThrottle;
-
- // The time taken to render (i.e. BitBlt) frames controls which component
- // needs to degrade. If the blt is expensive, the renderer degrades.
- // If the blt is cheap it's done anyway and the supplier degrades.
- int m_trRenderAvg; // Time frames are taking to blt
- int m_trRenderLast; // Time for last frame blt
- int m_tRenderStart; // Just before we started drawing (mSec)
- // derived from timeGetTime.
-
- // When frames are dropped we will play the next frame as early as we can.
- // If it was a false alarm and the machine is fast we slide gently back to
- // normal timing. To do this, we record the offset showing just how early
- // we really are. This will normally be negative meaning early or zero.
- int m_trEarliness;
-
- // Target provides slow long-term feedback to try to reduce the
- // average sync offset to zero. Whenever a frame is actually rendered
- // early we add a msec or two, whenever late we take off a few.
- // We add or take off 1/32 of the error time.
- // Eventually we should be hovering around zero. For a really bad case
- // where we were (say) 300mSec off, it might take 100 odd frames to
- // settle down. The rate of change of this is intended to be slower
- // than any other mechanism in Quartz, thereby avoiding hunting.
- int m_trTarget;
-
- // The proportion of time spent waiting for the right moment to blt
- // controls whether we bother to drop a frame or whether we reckon that
- // we're doing well enough that we can stand a one-frame glitch.
- int m_trWaitAvg; // Average of last few wait times
- // (actually we just average how early
- // we were). Negative here means LATE.
-
- // The average inter-frame time.
- // This is used to calculate the proportion of the time used by the
- // three operations (supplying us, waiting, rendering)
- int m_trFrameAvg; // Average inter-frame time
- int m_trDuration; // duration of last frame.
-
-#ifdef PERF
- // Performance logging identifiers
- int m_idTimeStamp; // MSR_id for frame time stamp
- int m_idEarliness; // MSR_id for earliness fudge
- int m_idTarget; // MSR_id for Target fudge
- int m_idWaitReal; // MSR_id for true wait time
- int m_idWait; // MSR_id for wait time recorded
- int m_idFrameAccuracy; // MSR_id for time frame is late (int)
- int m_idRenderAvg; // MSR_id for Render time recorded (int)
- int m_idSchLateTime; // MSR_id for lateness at scheduler
- int m_idQualityRate; // MSR_id for Quality rate requested
- int m_idQualityTime; // MSR_id for Quality time requested
- int m_idDecision; // MSR_id for decision code
- int m_idDuration; // MSR_id for duration of a frame
- int m_idThrottle; // MSR_id for audio-video throttling
- //int m_idDebug; // MSR_id for trace style debugging
- //int m_idSendQuality; // MSR_id for timing the notifications per se
-#endif // PERF
- REFERENCE_TIME m_trRememberStampForPerf; // original time stamp of frame
- // with no earliness fudges etc.
-#ifdef PERF
- REFERENCE_TIME m_trRememberFrameForPerf; // time when previous frame rendered
-
- // debug...
- int m_idFrameAvg;
- int m_idWaitAvg;
-#endif
-
- // PROPERTY PAGE
- // This has edit fields that show the user what's happening
- // These member variables hold these counts.
-
- int m_cFramesDropped; // cumulative frames dropped IN THE RENDERER
- int m_cFramesDrawn; // Frames since streaming started seen BY THE
- // RENDERER (some may be dropped upstream)
-
- // Next two support average sync offset and standard deviation of sync offset.
- LONGLONG m_iTotAcc; // Sum of accuracies in mSec
- LONGLONG m_iSumSqAcc; // Sum of squares of (accuracies in mSec)
-
- // Next two allow jitter calculation. Jitter is std deviation of frame time.
- REFERENCE_TIME m_trLastDraw; // Time of prev frame (for inter-frame times)
- LONGLONG m_iSumSqFrameTime; // Sum of squares of (inter-frame time in mSec)
- LONGLONG m_iSumFrameTime; // Sum of inter-frame times in mSec
-
- // To get performance statistics on frame rate, jitter etc, we need
- // to record the lateness and inter-frame time. What we actually need are the
- // data above (sum, sum of squares and number of entries for each) but the data
- // is generated just ahead of time and only later do we discover whether the
- // frame was actually drawn or not. So we have to hang on to the data
- int m_trLate; // hold onto frame lateness
- int m_trFrame; // hold onto inter-frame time
-
- int m_tStreamingStart; // if streaming then time streaming started
- // else time of last streaming session
- // used for property page statistics
-#ifdef PERF
- LONGLONG m_llTimeOffset; // timeGetTime()*10000+m_llTimeOffset==ref time
-#endif
-
-public:
-
-
- CBaseVideoRenderer(REFCLSID RenderClass, // CLSID for this renderer
- TCHAR *pName, // Debug ONLY description
- LPUNKNOWN pUnk, // Aggregated owner object
- HRESULT *phr); // General OLE return code
-
- ~CBaseVideoRenderer();
-
- // IQualityControl methods - Notify allows audio-video throttling
-
- STDMETHODIMP SetSink( IQualityControl * piqc);
- STDMETHODIMP Notify( IBaseFilter * pSelf, Quality q);
-
- // These provide a full video quality management implementation
-
- void OnRenderStart(IMediaSample *pMediaSample);
- void OnRenderEnd(IMediaSample *pMediaSample);
- void OnWaitStart();
- void OnWaitEnd();
- HRESULT OnStartStreaming();
- HRESULT OnStopStreaming();
- void ThrottleWait();
-
- // Handle the statistics gathering for our quality management
-
- void PreparePerformanceData(int trLate, int trFrame);
- virtual void RecordFrameLateness(int trLate, int trFrame);
- virtual void OnDirectRender(IMediaSample *pMediaSample);
- virtual HRESULT ResetStreamingTimes();
- BOOL ScheduleSample(IMediaSample *pMediaSample);
- HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,
- REFERENCE_TIME *ptrStart,
- REFERENCE_TIME *ptrEnd);
-
- virtual HRESULT SendQuality(REFERENCE_TIME trLate, REFERENCE_TIME trRealStream);
- STDMETHODIMP JoinFilterGraph(IFilterGraph * pGraph, LPCWSTR pName);
-
- //
- // Do estimates for standard deviations for per-frame
- // statistics
- //
- // *piResult = (llSumSq - iTot * iTot / m_cFramesDrawn - 1) /
- // (m_cFramesDrawn - 2)
- // or 0 if m_cFramesDrawn <= 3
- //
- HRESULT GetStdDev(
- int nSamples,
- int *piResult,
- LONGLONG llSumSq,
- LONGLONG iTot
- );
-public:
-
- // IQualProp property page support
-
- STDMETHODIMP get_FramesDroppedInRenderer(int *cFramesDropped);
- STDMETHODIMP get_FramesDrawn(int *pcFramesDrawn);
- STDMETHODIMP get_AvgFrameRate(int *piAvgFrameRate);
- STDMETHODIMP get_Jitter(int *piJitter);
- STDMETHODIMP get_AvgSyncOffset(int *piAvg);
- STDMETHODIMP get_DevSyncOffset(int *piDev);
-
- // Implement an IUnknown interface and expose IQualProp
-
- DECLARE_IUNKNOWN
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,VOID **ppv);
-};
-
-#endif // __RENBASE__
-
+//------------------------------------------------------------------------------
+// File: RenBase.h
+//
+// Desc: DirectShow base classes - defines a generic ActiveX base renderer
+// class.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __RENBASE__
+#define __RENBASE__
+
+// Forward class declarations
+
+class CBaseRenderer;
+class CBaseVideoRenderer;
+class CRendererInputPin;
+
+// This is our input pin class that channels calls to the renderer
+
+class CRendererInputPin : public CBaseInputPin
+{
+protected:
+
+ CBaseRenderer *m_pRenderer;
+
+public:
+
+ CRendererInputPin(__inout CBaseRenderer *pRenderer,
+ __inout HRESULT *phr,
+ __in_opt LPCWSTR Name);
+
+ // Overriden from the base pin classes
+
+ HRESULT BreakConnect();
+ HRESULT CompleteConnect(IPin *pReceivePin);
+ HRESULT SetMediaType(const CMediaType *pmt);
+ HRESULT CheckMediaType(const CMediaType *pmt);
+ HRESULT Active();
+ HRESULT Inactive();
+
+ // Add rendering behaviour to interface functions
+
+ STDMETHODIMP QueryId(__deref_out LPWSTR *Id);
+ STDMETHODIMP EndOfStream();
+ STDMETHODIMP BeginFlush();
+ STDMETHODIMP EndFlush();
+ STDMETHODIMP Receive(IMediaSample *pMediaSample);
+
+ // Helper
+ IMemAllocator inline *Allocator() const
+ {
+ return m_pAllocator;
+ }
+};
+
+// Main renderer class that handles synchronisation and state changes
+
+class CBaseRenderer : public CBaseFilter
+{
+protected:
+
+ friend class CRendererInputPin;
+
+ friend void CALLBACK EndOfStreamTimer(UINT uID, // Timer identifier
+ UINT uMsg, // Not currently used
+ DWORD_PTR dwUser, // User information
+ DWORD_PTR dw1, // Windows reserved
+ DWORD_PTR dw2); // Is also reserved
+
+ CRendererPosPassThru *m_pPosition; // Media seeking pass by object
+ CAMEvent m_RenderEvent; // Used to signal timer events
+ CAMEvent m_ThreadSignal; // Signalled to release worker thread
+ CAMEvent m_evComplete; // Signalled when state complete
+ BOOL m_bAbort; // Stop us from rendering more data
+ BOOL m_bStreaming; // Are we currently streaming
+ DWORD_PTR m_dwAdvise; // Timer advise cookie
+ IMediaSample *m_pMediaSample; // Current image media sample
+ BOOL m_bEOS; // Any more samples in the stream
+ BOOL m_bEOSDelivered; // Have we delivered an EC_COMPLETE
+ CRendererInputPin *m_pInputPin; // Our renderer input pin object
+ CCritSec m_InterfaceLock; // Critical section for interfaces
+ CCritSec m_RendererLock; // Controls access to internals
+ IQualityControl * m_pQSink; // QualityControl sink
+ BOOL m_bRepaintStatus; // Can we signal an EC_REPAINT
+ // Avoid some deadlocks by tracking filter during stop
+ volatile BOOL m_bInReceive; // Inside Receive between PrepareReceive
+ // And actually processing the sample
+ REFERENCE_TIME m_SignalTime; // Time when we signal EC_COMPLETE
+ UINT m_EndOfStreamTimer; // Used to signal end of stream
+ CCritSec m_ObjectCreationLock; // This lock protects the creation and
+ // of m_pPosition and m_pInputPin. It
+ // ensures that two threads cannot create
+ // either object simultaneously.
+
+public:
+
+ CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer
+ __in_opt LPCTSTR pName, // Debug ONLY description
+ __inout_opt LPUNKNOWN pUnk, // Aggregated owner object
+ __inout HRESULT *phr); // General OLE return code
+
+ ~CBaseRenderer();
+
+ // Overriden to say what interfaces we support and where
+
+ virtual HRESULT GetMediaPositionInterface(REFIID riid, __deref_out void **ppv);
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **);
+
+ virtual HRESULT SourceThreadCanWait(BOOL bCanWait);
+
+#ifdef DEBUG
+ // Debug only dump of the renderer state
+ void DisplayRendererState();
+#endif
+ virtual HRESULT WaitForRenderTime();
+ virtual HRESULT CompleteStateChange(FILTER_STATE OldState);
+
+ // Return internal information about this filter
+
+ BOOL IsEndOfStream() { return m_bEOS; };
+ BOOL IsEndOfStreamDelivered() { return m_bEOSDelivered; };
+ BOOL IsStreaming() { return m_bStreaming; };
+ void SetAbortSignal(BOOL bAbort) { m_bAbort = bAbort; };
+ virtual void OnReceiveFirstSample(IMediaSample *pMediaSample) { };
+ CAMEvent *GetRenderEvent() { return &m_RenderEvent; };
+
+ // Permit access to the transition state
+
+ void Ready() { m_evComplete.Set(); };
+ void NotReady() { m_evComplete.Reset(); };
+ BOOL CheckReady() { return m_evComplete.Check(); };
+
+ virtual int GetPinCount();
+ virtual CBasePin *GetPin(int n);
+ FILTER_STATE GetRealState();
+ void SendRepaint();
+ void SendNotifyWindow(IPin *pPin,HWND hwnd);
+ BOOL OnDisplayChange();
+ void SetRepaintStatus(BOOL bRepaint);
+
+ // Override the filter and pin interface functions
+
+ STDMETHODIMP Stop();
+ STDMETHODIMP Pause();
+ STDMETHODIMP Run(REFERENCE_TIME StartTime);
+ STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State);
+ STDMETHODIMP FindPin(LPCWSTR Id, __deref_out IPin **ppPin);
+
+ // These are available for a quality management implementation
+
+ virtual void OnRenderStart(IMediaSample *pMediaSample);
+ virtual void OnRenderEnd(IMediaSample *pMediaSample);
+ virtual HRESULT OnStartStreaming() { return NOERROR; };
+ virtual HRESULT OnStopStreaming() { return NOERROR; };
+ virtual void OnWaitStart() { };
+ virtual void OnWaitEnd() { };
+ virtual void PrepareRender() { };
+
+#ifdef PERF
+ REFERENCE_TIME m_trRenderStart; // Just before we started drawing
+ // Set in OnRenderStart, Used in OnRenderEnd
+ int m_idBaseStamp; // MSR_id for frame time stamp
+ int m_idBaseRenderTime; // MSR_id for true wait time
+ int m_idBaseAccuracy; // MSR_id for time frame is late (int)
+#endif
+
+ // Quality management implementation for scheduling rendering
+
+ virtual BOOL ScheduleSample(IMediaSample *pMediaSample);
+ virtual HRESULT GetSampleTimes(IMediaSample *pMediaSample,
+ __out REFERENCE_TIME *pStartTime,
+ __out REFERENCE_TIME *pEndTime);
+
+ virtual HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,
+ __out REFERENCE_TIME *ptrStart,
+ __out REFERENCE_TIME *ptrEnd);
+
+ // Lots of end of stream complexities
+
+ void TimerCallback();
+ void ResetEndOfStreamTimer();
+ HRESULT NotifyEndOfStream();
+ virtual HRESULT SendEndOfStream();
+ virtual HRESULT ResetEndOfStream();
+ virtual HRESULT EndOfStream();
+
+ // Rendering is based around the clock
+
+ void SignalTimerFired();
+ virtual HRESULT CancelNotification();
+ virtual HRESULT ClearPendingSample();
+
+ // Called when the filter changes state
+
+ virtual HRESULT Active();
+ virtual HRESULT Inactive();
+ virtual HRESULT StartStreaming();
+ virtual HRESULT StopStreaming();
+ virtual HRESULT BeginFlush();
+ virtual HRESULT EndFlush();
+
+ // Deal with connections and type changes
+
+ virtual HRESULT BreakConnect();
+ virtual HRESULT SetMediaType(const CMediaType *pmt);
+ virtual HRESULT CompleteConnect(IPin *pReceivePin);
+
+ // These look after the handling of data samples
+
+ virtual HRESULT PrepareReceive(IMediaSample *pMediaSample);
+ virtual HRESULT Receive(IMediaSample *pMediaSample);
+ virtual BOOL HaveCurrentSample();
+ virtual IMediaSample *GetCurrentSample();
+ virtual HRESULT Render(IMediaSample *pMediaSample);
+
+ // Derived classes MUST override these
+ virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE;
+ virtual HRESULT CheckMediaType(const CMediaType *) PURE;
+
+ // Helper
+ void WaitForReceiveToComplete();
+};
+
+
+// CBaseVideoRenderer is a renderer class (see its ancestor class) and
+// it handles scheduling of media samples so that they are drawn at the
+// correct time by the reference clock. It implements a degradation
+// strategy. Possible degradation modes are:
+// Drop frames here (only useful if the drawing takes significant time)
+// Signal supplier (upstream) to drop some frame(s) - i.e. one-off skip.
+// Signal supplier to change the frame rate - i.e. ongoing skipping.
+// Or any combination of the above.
+// In order to determine what's useful to try we need to know what's going
+// on. This is done by timing various operations (including the supplier).
+// This timing is done by using timeGetTime as it is accurate enough and
+// usually cheaper than calling the reference clock. It also tells the
+// truth if there is an audio break and the reference clock stops.
+// We provide a number of public entry points (named OnXxxStart, OnXxxEnd)
+// which the rest of the renderer calls at significant moments. These do
+// the timing.
+
+// the number of frames that the sliding averages are averaged over.
+// the rule is (1024*NewObservation + (AVGPERIOD-1) * PreviousAverage)/AVGPERIOD
+#define AVGPERIOD 4
+#define DO_MOVING_AVG(avg,obs) (avg = (1024*obs + (AVGPERIOD-1)*avg)/AVGPERIOD)
+// Spot the bug in this macro - I can't. but it doesn't work!
+
+class CBaseVideoRenderer : public CBaseRenderer, // Base renderer class
+ public IQualProp, // Property page guff
+ public IQualityControl // Allow throttling
+{
+protected:
+
+ // Hungarian:
+ // tFoo is the time Foo in mSec (beware m_tStart from filter.h)
+ // trBar is the time Bar by the reference clock
+
+ //******************************************************************
+ // State variables to control synchronisation
+ //******************************************************************
+
+ // Control of sending Quality messages. We need to know whether
+ // we are in trouble (e.g. frames being dropped) and where the time
+ // is being spent.
+
+ // When we drop a frame we play the next one early.
+ // The frame after that is likely to wait before drawing and counting this
+ // wait as spare time is unfair, so we count it as a zero wait.
+ // We therefore need to know whether we are playing frames early or not.
+
+ int m_nNormal; // The number of consecutive frames
+ // drawn at their normal time (not early)
+ // -1 means we just dropped a frame.
+
+#ifdef PERF
+ BOOL m_bDrawLateFrames; // Don't drop any frames (debug and I'm
+ // not keen on people using it!)
+#endif
+
+ BOOL m_bSupplierHandlingQuality;// The response to Quality messages says
+ // our supplier is handling things.
+ // We will allow things to go extra late
+ // before dropping frames. We will play
+ // very early after he has dropped one.
+
+ // Control of scheduling, frame dropping etc.
+ // We need to know where the time is being spent so as to tell whether
+ // we should be taking action here, signalling supplier or what.
+ // The variables are initialised to a mode of NOT dropping frames.
+ // They will tell the truth after a few frames.
+ // We typically record a start time for an event, later we get the time
+ // again and subtract to get the elapsed time, and we average this over
+ // a few frames. The average is used to tell what mode we are in.
+
+ // Although these are reference times (64 bit) they are all DIFFERENCES
+ // between times which are small. An int will go up to 214 secs before
+ // overflow. Avoiding 64 bit multiplications and divisions seems
+ // worth while.
+
+
+
+ // Audio-video throttling. If the user has turned up audio quality
+ // very high (in principle it could be any other stream, not just audio)
+ // then we can receive cries for help via the graph manager. In this case
+ // we put in a wait for some time after rendering each frame.
+ int m_trThrottle;
+
+ // The time taken to render (i.e. BitBlt) frames controls which component
+ // needs to degrade. If the blt is expensive, the renderer degrades.
+ // If the blt is cheap it's done anyway and the supplier degrades.
+ int m_trRenderAvg; // Time frames are taking to blt
+ int m_trRenderLast; // Time for last frame blt
+ int m_tRenderStart; // Just before we started drawing (mSec)
+ // derived from timeGetTime.
+
+ // When frames are dropped we will play the next frame as early as we can.
+ // If it was a false alarm and the machine is fast we slide gently back to
+ // normal timing. To do this, we record the offset showing just how early
+ // we really are. This will normally be negative meaning early or zero.
+ int m_trEarliness;
+
+ // Target provides slow long-term feedback to try to reduce the
+ // average sync offset to zero. Whenever a frame is actually rendered
+ // early we add a msec or two, whenever late we take off a few.
+ // We add or take off 1/32 of the error time.
+ // Eventually we should be hovering around zero. For a really bad case
+ // where we were (say) 300mSec off, it might take 100 odd frames to
+ // settle down. The rate of change of this is intended to be slower
+ // than any other mechanism in Quartz, thereby avoiding hunting.
+ int m_trTarget;
+
+ // The proportion of time spent waiting for the right moment to blt
+ // controls whether we bother to drop a frame or whether we reckon that
+ // we're doing well enough that we can stand a one-frame glitch.
+ int m_trWaitAvg; // Average of last few wait times
+ // (actually we just average how early
+ // we were). Negative here means LATE.
+
+ // The average inter-frame time.
+ // This is used to calculate the proportion of the time used by the
+ // three operations (supplying us, waiting, rendering)
+ int m_trFrameAvg; // Average inter-frame time
+ int m_trDuration; // duration of last frame.
+
+#ifdef PERF
+ // Performance logging identifiers
+ int m_idTimeStamp; // MSR_id for frame time stamp
+ int m_idEarliness; // MSR_id for earliness fudge
+ int m_idTarget; // MSR_id for Target fudge
+ int m_idWaitReal; // MSR_id for true wait time
+ int m_idWait; // MSR_id for wait time recorded
+ int m_idFrameAccuracy; // MSR_id for time frame is late (int)
+ int m_idRenderAvg; // MSR_id for Render time recorded (int)
+ int m_idSchLateTime; // MSR_id for lateness at scheduler
+ int m_idQualityRate; // MSR_id for Quality rate requested
+ int m_idQualityTime; // MSR_id for Quality time requested
+ int m_idDecision; // MSR_id for decision code
+ int m_idDuration; // MSR_id for duration of a frame
+ int m_idThrottle; // MSR_id for audio-video throttling
+ //int m_idDebug; // MSR_id for trace style debugging
+ //int m_idSendQuality; // MSR_id for timing the notifications per se
+#endif // PERF
+ REFERENCE_TIME m_trRememberStampForPerf; // original time stamp of frame
+ // with no earliness fudges etc.
+#ifdef PERF
+ REFERENCE_TIME m_trRememberFrameForPerf; // time when previous frame rendered
+
+ // debug...
+ int m_idFrameAvg;
+ int m_idWaitAvg;
+#endif
+
+ // PROPERTY PAGE
+ // This has edit fields that show the user what's happening
+ // These member variables hold these counts.
+
+ int m_cFramesDropped; // cumulative frames dropped IN THE RENDERER
+ int m_cFramesDrawn; // Frames since streaming started seen BY THE
+ // RENDERER (some may be dropped upstream)
+
+ // Next two support average sync offset and standard deviation of sync offset.
+ LONGLONG m_iTotAcc; // Sum of accuracies in mSec
+ LONGLONG m_iSumSqAcc; // Sum of squares of (accuracies in mSec)
+
+ // Next two allow jitter calculation. Jitter is std deviation of frame time.
+ REFERENCE_TIME m_trLastDraw; // Time of prev frame (for inter-frame times)
+ LONGLONG m_iSumSqFrameTime; // Sum of squares of (inter-frame time in mSec)
+ LONGLONG m_iSumFrameTime; // Sum of inter-frame times in mSec
+
+ // To get performance statistics on frame rate, jitter etc, we need
+ // to record the lateness and inter-frame time. What we actually need are the
+ // data above (sum, sum of squares and number of entries for each) but the data
+ // is generated just ahead of time and only later do we discover whether the
+ // frame was actually drawn or not. So we have to hang on to the data
+ int m_trLate; // hold onto frame lateness
+ int m_trFrame; // hold onto inter-frame time
+
+ int m_tStreamingStart; // if streaming then time streaming started
+ // else time of last streaming session
+ // used for property page statistics
+#ifdef PERF
+ LONGLONG m_llTimeOffset; // timeGetTime()*10000+m_llTimeOffset==ref time
+#endif
+
+public:
+
+
+ CBaseVideoRenderer(REFCLSID RenderClass, // CLSID for this renderer
+ __in_opt LPCTSTR pName, // Debug ONLY description
+ __inout_opt LPUNKNOWN pUnk, // Aggregated owner object
+ __inout HRESULT *phr); // General OLE return code
+
+ ~CBaseVideoRenderer();
+
+ // IQualityControl methods - Notify allows audio-video throttling
+
+ STDMETHODIMP SetSink( IQualityControl * piqc);
+ STDMETHODIMP Notify( IBaseFilter * pSelf, Quality q);
+
+ // These provide a full video quality management implementation
+
+ void OnRenderStart(IMediaSample *pMediaSample);
+ void OnRenderEnd(IMediaSample *pMediaSample);
+ void OnWaitStart();
+ void OnWaitEnd();
+ HRESULT OnStartStreaming();
+ HRESULT OnStopStreaming();
+ void ThrottleWait();
+
+ // Handle the statistics gathering for our quality management
+
+ void PreparePerformanceData(int trLate, int trFrame);
+ virtual void RecordFrameLateness(int trLate, int trFrame);
+ virtual void OnDirectRender(IMediaSample *pMediaSample);
+ virtual HRESULT ResetStreamingTimes();
+ BOOL ScheduleSample(IMediaSample *pMediaSample);
+ HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,
+ __inout REFERENCE_TIME *ptrStart,
+ __inout REFERENCE_TIME *ptrEnd);
+
+ virtual HRESULT SendQuality(REFERENCE_TIME trLate, REFERENCE_TIME trRealStream);
+ STDMETHODIMP JoinFilterGraph(__inout_opt IFilterGraph * pGraph, __in_opt LPCWSTR pName);
+
+ //
+ // Do estimates for standard deviations for per-frame
+ // statistics
+ //
+ // *piResult = (llSumSq - iTot * iTot / m_cFramesDrawn - 1) /
+ // (m_cFramesDrawn - 2)
+ // or 0 if m_cFramesDrawn <= 3
+ //
+ HRESULT GetStdDev(
+ int nSamples,
+ __out int *piResult,
+ LONGLONG llSumSq,
+ LONGLONG iTot
+ );
+public:
+
+ // IQualProp property page support
+
+ STDMETHODIMP get_FramesDroppedInRenderer(__out int *cFramesDropped);
+ STDMETHODIMP get_FramesDrawn(__out int *pcFramesDrawn);
+ STDMETHODIMP get_AvgFrameRate(__out int *piAvgFrameRate);
+ STDMETHODIMP get_Jitter(__out int *piJitter);
+ STDMETHODIMP get_AvgSyncOffset(__out int *piAvg);
+ STDMETHODIMP get_DevSyncOffset(__out int *piDev);
+
+ // Implement an IUnknown interface and expose IQualProp
+
+ DECLARE_IUNKNOWN
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out VOID **ppv);
+};
+
+#endif // __RENBASE__
+
diff --git a/dshow_base/schedule.cpp b/dshow_base/schedule.cpp
new file mode 100644
index 0000000..7d79830
--- /dev/null
+++ b/dshow_base/schedule.cpp
@@ -0,0 +1,284 @@
+//------------------------------------------------------------------------------
+// File: Schedule.cpp
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+
+// DbgLog values (all on LOG_TIMING):
+//
+// 2 for schedulting, firing and shunting of events
+// 3 for wait delays and wake-up times of event thread
+// 4 for details of whats on the list when the thread awakes
+
+/* Construct & destructors */
+
+CAMSchedule::CAMSchedule( HANDLE ev )
+: CBaseObject(TEXT("CAMSchedule"))
+, head(&z, 0), z(0, MAX_TIME)
+, m_dwNextCookie(0), m_dwAdviseCount(0)
+, m_pAdviseCache(0), m_dwCacheCount(0)
+, m_ev( ev )
+{
+ head.m_dwAdviseCookie = z.m_dwAdviseCookie = 0;
+}
+
+CAMSchedule::~CAMSchedule()
+{
+ m_Serialize.Lock();
+
+ // Delete cache
+ CAdvisePacket * p = m_pAdviseCache;
+ while (p)
+ {
+ CAdvisePacket *const p_next = p->m_next;
+ delete p;
+ p = p_next;
+ }
+
+ ASSERT( m_dwAdviseCount == 0 );
+ // Better to be safe than sorry
+ if ( m_dwAdviseCount > 0 )
+ {
+ DumpLinkedList();
+ while ( !head.m_next->IsZ() )
+ {
+ head.DeleteNext();
+ --m_dwAdviseCount;
+ }
+ }
+
+ // If, in the debug version, we assert twice, it means, not only
+ // did we have left over advises, but we have also let m_dwAdviseCount
+ // get out of sync. with the number of advises actually on the list.
+ ASSERT( m_dwAdviseCount == 0 );
+
+ m_Serialize.Unlock();
+}
+
+/* Public methods */
+
+DWORD CAMSchedule::GetAdviseCount()
+{
+ // No need to lock, m_dwAdviseCount is 32bits & declared volatile
+ return m_dwAdviseCount;
+}
+
+REFERENCE_TIME CAMSchedule::GetNextAdviseTime()
+{
+ CAutoLock lck(&m_Serialize); // Need to stop the linked list from changing
+ return head.m_next->m_rtEventTime;
+}
+
+DWORD_PTR CAMSchedule::AddAdvisePacket
+( const REFERENCE_TIME & time1
+, const REFERENCE_TIME & time2
+, HANDLE h, BOOL periodic
+)
+{
+ // Since we use MAX_TIME as a sentry, we can't afford to
+ // schedule a notification at MAX_TIME
+ ASSERT( time1 < MAX_TIME );
+ DWORD_PTR Result;
+ CAdvisePacket * p;
+
+ m_Serialize.Lock();
+
+ if (m_pAdviseCache)
+ {
+ p = m_pAdviseCache;
+ m_pAdviseCache = p->m_next;
+ --m_dwCacheCount;
+ }
+ else
+ {
+ p = new CAdvisePacket();
+ }
+ if (p)
+ {
+ p->m_rtEventTime = time1; p->m_rtPeriod = time2;
+ p->m_hNotify = h; p->m_bPeriodic = periodic;
+ Result = AddAdvisePacket( p );
+ }
+ else Result = 0;
+
+ m_Serialize.Unlock();
+
+ return Result;
+}
+
+HRESULT CAMSchedule::Unadvise(DWORD_PTR dwAdviseCookie)
+{
+ HRESULT hr = S_FALSE;
+ CAdvisePacket * p_prev = &head;
+ CAdvisePacket * p_n;
+ m_Serialize.Lock();
+ while ( p_n = p_prev->Next() ) // The Next() method returns NULL when it hits z
+ {
+ if ( p_n->m_dwAdviseCookie == dwAdviseCookie )
+ {
+ Delete( p_prev->RemoveNext() );
+ --m_dwAdviseCount;
+ hr = S_OK;
+ // Having found one cookie that matches, there should be no more
+ #ifdef DEBUG
+ while (p_n = p_prev->Next())
+ {
+ ASSERT(p_n->m_dwAdviseCookie != dwAdviseCookie);
+ p_prev = p_n;
+ }
+ #endif
+ break;
+ }
+ p_prev = p_n;
+ };
+ m_Serialize.Unlock();
+ return hr;
+}
+
+REFERENCE_TIME CAMSchedule::Advise( const REFERENCE_TIME & rtTime )
+{
+ REFERENCE_TIME rtNextTime;
+ CAdvisePacket * pAdvise;
+
+ DbgLog((LOG_TIMING, 2,
+ TEXT("CAMSchedule::Advise( %lu ms )"), ULONG(rtTime / (UNITS / MILLISECONDS))));
+
+ CAutoLock lck(&m_Serialize);
+
+ #ifdef DEBUG
+ if (DbgCheckModuleLevel(LOG_TIMING, 4)) DumpLinkedList();
+ #endif
+
+ // Note - DON'T cache the difference, it might overflow
+ while ( rtTime >= (rtNextTime = (pAdvise=head.m_next)->m_rtEventTime) &&
+ !pAdvise->IsZ() )
+ {
+ ASSERT(pAdvise->m_dwAdviseCookie); // If this is zero, its the head or the tail!!
+
+ ASSERT(pAdvise->m_hNotify != INVALID_HANDLE_VALUE);
+
+ if (pAdvise->m_bPeriodic == TRUE)
+ {
+ ReleaseSemaphore(pAdvise->m_hNotify,1,NULL);
+ pAdvise->m_rtEventTime += pAdvise->m_rtPeriod;
+ ShuntHead();
+ }
+ else
+ {
+ ASSERT( pAdvise->m_bPeriodic == FALSE );
+ EXECUTE_ASSERT(SetEvent(pAdvise->m_hNotify));
+ --m_dwAdviseCount;
+ Delete( head.RemoveNext() );
+ }
+
+ }
+
+ DbgLog((LOG_TIMING, 3,
+ TEXT("CAMSchedule::Advise() Next time stamp: %lu ms, for advise %lu."),
+ DWORD(rtNextTime / (UNITS / MILLISECONDS)), pAdvise->m_dwAdviseCookie ));
+
+ return rtNextTime;
+}
+
+/* Private methods */
+
+DWORD_PTR CAMSchedule::AddAdvisePacket( __inout CAdvisePacket * pPacket )
+{
+ ASSERT(pPacket->m_rtEventTime >= 0 && pPacket->m_rtEventTime < MAX_TIME);
+ ASSERT(CritCheckIn(&m_Serialize));
+
+ CAdvisePacket * p_prev = &head;
+ CAdvisePacket * p_n;
+
+ const DWORD_PTR Result = pPacket->m_dwAdviseCookie = ++m_dwNextCookie;
+ // This relies on the fact that z is a sentry with a maximal m_rtEventTime
+ for(;;p_prev = p_n)
+ {
+ p_n = p_prev->m_next;
+ if ( p_n->m_rtEventTime >= pPacket->m_rtEventTime ) break;
+ }
+ p_prev->InsertAfter( pPacket );
+ ++m_dwAdviseCount;
+
+ DbgLog((LOG_TIMING, 2, TEXT("Added advise %lu, for thread 0x%02X, scheduled at %lu"),
+ pPacket->m_dwAdviseCookie, GetCurrentThreadId(), (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) ));
+
+ // If packet added at the head, then clock needs to re-evaluate wait time.
+ if ( p_prev == &head ) SetEvent( m_ev );
+
+ return Result;
+}
+
+void CAMSchedule::Delete( __inout CAdvisePacket * pPacket )
+{
+ if ( m_dwCacheCount >= dwCacheMax ) delete pPacket;
+ else
+ {
+ m_Serialize.Lock();
+ pPacket->m_next = m_pAdviseCache;
+ m_pAdviseCache = pPacket;
+ ++m_dwCacheCount;
+ m_Serialize.Unlock();
+ }
+}
+
+
+// Takes the head of the list & repositions it
+void CAMSchedule::ShuntHead()
+{
+ CAdvisePacket * p_prev = &head;
+ CAdvisePacket * p_n;
+
+ m_Serialize.Lock();
+ CAdvisePacket *const pPacket = head.m_next;
+
+ // This will catch both an empty list,
+ // and if somehow a MAX_TIME time gets into the list
+ // (which would also break this method).
+ ASSERT( pPacket->m_rtEventTime < MAX_TIME );
+
+ // This relies on the fact that z is a sentry with a maximal m_rtEventTime
+ for(;;p_prev = p_n)
+ {
+ p_n = p_prev->m_next;
+ if ( p_n->m_rtEventTime > pPacket->m_rtEventTime ) break;
+ }
+ // If p_prev == pPacket then we're already in the right place
+ if (p_prev != pPacket)
+ {
+ head.m_next = pPacket->m_next;
+ (p_prev->m_next = pPacket)->m_next = p_n;
+ }
+ #ifdef DEBUG
+ DbgLog((LOG_TIMING, 2, TEXT("Periodic advise %lu, shunted to %lu"),
+ pPacket->m_dwAdviseCookie, (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) ));
+ #endif
+ m_Serialize.Unlock();
+}
+
+
+#ifdef DEBUG
+void CAMSchedule::DumpLinkedList()
+{
+ m_Serialize.Lock();
+ int i=0;
+ DbgLog((LOG_TIMING, 1, TEXT("CAMSchedule::DumpLinkedList() this = 0x%p"), this));
+ for ( CAdvisePacket * p = &head
+ ; p
+ ; p = p->m_next , i++
+ )
+ {
+ DbgLog((LOG_TIMING, 1, TEXT("Advise List # %lu, Cookie %d, RefTime %lu"),
+ i,
+ p->m_dwAdviseCookie,
+ p->m_rtEventTime / (UNITS / MILLISECONDS)
+ ));
+ }
+ m_Serialize.Unlock();
+}
+#endif
diff --git a/dshow_base/schedule.h b/dshow_base/schedule.h
index e787307..c16700a 100644
--- a/dshow_base/schedule.h
+++ b/dshow_base/schedule.h
@@ -1,128 +1,128 @@
-//------------------------------------------------------------------------------
-// File: Schedule.h
-//
-// Desc: DirectShow base classes.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __CAMSchedule__
-#define __CAMSchedule__
-
-class CAMSchedule : private CBaseObject
-{
-public:
- virtual ~CAMSchedule();
- // ev is the event we should fire if the advise time needs re-evaluating
- CAMSchedule( HANDLE ev );
-
- DWORD GetAdviseCount();
- REFERENCE_TIME GetNextAdviseTime();
-
- // We need a method for derived classes to add advise packets, we return the cookie
- DWORD_PTR AddAdvisePacket( const REFERENCE_TIME & time1, const REFERENCE_TIME & time2, HANDLE h, BOOL periodic );
- // And a way to cancel
- HRESULT Unadvise(DWORD_PTR dwAdviseCookie);
-
- // Tell us the time please, and we'll dispatch the expired events. We return the time of the next event.
- // NB: The time returned will be "useless" if you start adding extra Advises. But that's the problem of
- // whoever is using this helper class (typically a clock).
- REFERENCE_TIME Advise( const REFERENCE_TIME & rtTime );
-
- // Get the event handle which will be set if advise time requires re-evaluation.
- HANDLE GetEvent() const { return m_ev; }
-
-private:
- // We define the nodes that will be used in our singly linked list
- // of advise packets. The list is ordered by time, with the
- // elements that will expire first at the front.
- class CAdvisePacket
- {
- public:
- CAdvisePacket()
- {}
-
- CAdvisePacket * m_next;
- DWORD_PTR m_dwAdviseCookie;
- REFERENCE_TIME m_rtEventTime; // Time at which event should be set
- REFERENCE_TIME m_rtPeriod; // Periodic time
- HANDLE m_hNotify; // Handle to event or semephore
- BOOL m_bPeriodic; // TRUE => Periodic event
-
- CAdvisePacket( CAdvisePacket * next, LONGLONG time ) : m_next(next), m_rtEventTime(time)
- {}
-
- void InsertAfter( CAdvisePacket * p )
- {
- p->m_next = m_next;
- m_next = p;
- }
-
- int IsZ() const // That is, is it the node that represents the end of the list
- { return m_next == 0; }
-
- CAdvisePacket * RemoveNext()
- {
- CAdvisePacket *const next = m_next;
- CAdvisePacket *const new_next = next->m_next;
- m_next = new_next;
- return next;
- }
-
- void DeleteNext()
- {
- delete RemoveNext();
- }
-
- CAdvisePacket * Next() const
- {
- CAdvisePacket * result = m_next;
- if (result->IsZ()) result = 0;
- return result;
- }
-
- DWORD_PTR Cookie() const
- { return m_dwAdviseCookie; }
- };
-
- // Structure is:
- // head -> elmt1 -> elmt2 -> z -> null
- // So an empty list is: head -> z -> null
- // Having head & z as links makes insertaion,
- // deletion and shunting much easier.
- CAdvisePacket head, z; // z is both a tail and a sentry
-
- volatile DWORD_PTR m_dwNextCookie; // Strictly increasing
- volatile DWORD m_dwAdviseCount; // Number of elements on list
-
- CCritSec m_Serialize;
-
- // AddAdvisePacket: adds the packet, returns the cookie (0 if failed)
- DWORD_PTR AddAdvisePacket( CAdvisePacket * pPacket );
- // Event that we should set if the packed added above will be the next to fire.
- const HANDLE m_ev;
-
- // A Shunt is where we have changed the first element in the
- // list and want it re-evaluating (i.e. repositioned) in
- // the list.
- void ShuntHead();
-
- // Rather than delete advise packets, we cache them for future use
- CAdvisePacket * m_pAdviseCache;
- DWORD m_dwCacheCount;
- enum { dwCacheMax = 5 }; // Don't bother caching more than five
-
- void Delete( CAdvisePacket * pLink );// This "Delete" will cache the Link
-
-// Attributes and methods for debugging
-public:
-#ifdef DEBUG
- void DumpLinkedList();
-#else
- void DumpLinkedList() {}
-#endif
-
-};
-
-#endif // __CAMSchedule__
+//------------------------------------------------------------------------------
+// File: Schedule.h
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __CAMSchedule__
+#define __CAMSchedule__
+
+class CAMSchedule : private CBaseObject
+{
+public:
+ virtual ~CAMSchedule();
+ // ev is the event we should fire if the advise time needs re-evaluating
+ CAMSchedule( HANDLE ev );
+
+ DWORD GetAdviseCount();
+ REFERENCE_TIME GetNextAdviseTime();
+
+ // We need a method for derived classes to add advise packets, we return the cookie
+ DWORD_PTR AddAdvisePacket( const REFERENCE_TIME & time1, const REFERENCE_TIME & time2, HANDLE h, BOOL periodic );
+ // And a way to cancel
+ HRESULT Unadvise(DWORD_PTR dwAdviseCookie);
+
+ // Tell us the time please, and we'll dispatch the expired events. We return the time of the next event.
+ // NB: The time returned will be "useless" if you start adding extra Advises. But that's the problem of
+ // whoever is using this helper class (typically a clock).
+ REFERENCE_TIME Advise( const REFERENCE_TIME & rtTime );
+
+ // Get the event handle which will be set if advise time requires re-evaluation.
+ HANDLE GetEvent() const { return m_ev; }
+
+private:
+ // We define the nodes that will be used in our singly linked list
+ // of advise packets. The list is ordered by time, with the
+ // elements that will expire first at the front.
+ class CAdvisePacket
+ {
+ public:
+ CAdvisePacket()
+ {}
+
+ CAdvisePacket * m_next;
+ DWORD_PTR m_dwAdviseCookie;
+ REFERENCE_TIME m_rtEventTime; // Time at which event should be set
+ REFERENCE_TIME m_rtPeriod; // Periodic time
+ HANDLE m_hNotify; // Handle to event or semephore
+ BOOL m_bPeriodic; // TRUE => Periodic event
+
+ CAdvisePacket( __inout_opt CAdvisePacket * next, LONGLONG time ) : m_next(next), m_rtEventTime(time)
+ {}
+
+ void InsertAfter( __inout CAdvisePacket * p )
+ {
+ p->m_next = m_next;
+ m_next = p;
+ }
+
+ int IsZ() const // That is, is it the node that represents the end of the list
+ { return m_next == 0; }
+
+ CAdvisePacket * RemoveNext()
+ {
+ CAdvisePacket *const next = m_next;
+ CAdvisePacket *const new_next = next->m_next;
+ m_next = new_next;
+ return next;
+ }
+
+ void DeleteNext()
+ {
+ delete RemoveNext();
+ }
+
+ CAdvisePacket * Next() const
+ {
+ CAdvisePacket * result = m_next;
+ if (result->IsZ()) result = 0;
+ return result;
+ }
+
+ DWORD_PTR Cookie() const
+ { return m_dwAdviseCookie; }
+ };
+
+ // Structure is:
+ // head -> elmt1 -> elmt2 -> z -> null
+ // So an empty list is: head -> z -> null
+ // Having head & z as links makes insertaion,
+ // deletion and shunting much easier.
+ CAdvisePacket head, z; // z is both a tail and a sentry
+
+ volatile DWORD_PTR m_dwNextCookie; // Strictly increasing
+ volatile DWORD m_dwAdviseCount; // Number of elements on list
+
+ CCritSec m_Serialize;
+
+ // AddAdvisePacket: adds the packet, returns the cookie (0 if failed)
+ DWORD_PTR AddAdvisePacket( __inout CAdvisePacket * pPacket );
+ // Event that we should set if the packed added above will be the next to fire.
+ const HANDLE m_ev;
+
+ // A Shunt is where we have changed the first element in the
+ // list and want it re-evaluating (i.e. repositioned) in
+ // the list.
+ void ShuntHead();
+
+ // Rather than delete advise packets, we cache them for future use
+ CAdvisePacket * m_pAdviseCache;
+ DWORD m_dwCacheCount;
+ enum { dwCacheMax = 5 }; // Don't bother caching more than five
+
+ void Delete( __inout CAdvisePacket * pLink );// This "Delete" will cache the Link
+
+// Attributes and methods for debugging
+public:
+#ifdef DEBUG
+ void DumpLinkedList();
+#else
+ void DumpLinkedList() {}
+#endif
+
+};
+
+#endif // __CAMSchedule__
diff --git a/dshow_base/seekpt.cpp b/dshow_base/seekpt.cpp
new file mode 100644
index 0000000..bb13d6f
--- /dev/null
+++ b/dshow_base/seekpt.cpp
@@ -0,0 +1,83 @@
+//------------------------------------------------------------------------------
+// File: SeekPT.cpp
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#include "seekpt.h"
+
+//==================================================================
+// CreateInstance
+// This goes in the factory template table to create new instances
+// If there is already a mapper instance - return that, else make one
+// and save it in a static variable so that forever after we can return that.
+//==================================================================
+
+CUnknown * CSeekingPassThru::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)
+{
+ return new CSeekingPassThru(NAME("Seeking PassThru"),pUnk, phr);
+}
+
+
+STDMETHODIMP CSeekingPassThru::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)
+{
+ if (riid == IID_ISeekingPassThru) {
+ return GetInterface((ISeekingPassThru *) this, ppv);
+ } else {
+ if (m_pPosPassThru &&
+ (riid == IID_IMediaSeeking ||
+ riid == IID_IMediaPosition)) {
+ return m_pPosPassThru->NonDelegatingQueryInterface(riid,ppv);
+ } else {
+ return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+ }
+ }
+}
+
+
+CSeekingPassThru::CSeekingPassThru( __in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr )
+ : CUnknown(pName, pUnk, phr),
+ m_pPosPassThru(NULL)
+{
+}
+
+
+CSeekingPassThru::~CSeekingPassThru()
+{
+ delete m_pPosPassThru;
+}
+
+STDMETHODIMP CSeekingPassThru::Init(BOOL bRendererSeeking, IPin *pPin)
+{
+ HRESULT hr = NOERROR;
+ if (m_pPosPassThru) {
+ hr = E_FAIL;
+ } else {
+ m_pPosPassThru =
+ bRendererSeeking ?
+ new CRendererPosPassThru(
+ NAME("Render Seeking COM object"),
+ (IUnknown *)this,
+ &hr,
+ pPin) :
+ new CPosPassThru(
+ NAME("Render Seeking COM object"),
+ (IUnknown *)this,
+ &hr,
+ pPin);
+ if (!m_pPosPassThru) {
+ hr = E_OUTOFMEMORY;
+ } else {
+ if (FAILED(hr)) {
+ delete m_pPosPassThru;
+ m_pPosPassThru = NULL;
+ }
+ }
+ }
+ return hr;
+}
+
diff --git a/dshow_base/seekpt.h b/dshow_base/seekpt.h
index 1d26dfe..208d418 100644
--- a/dshow_base/seekpt.h
+++ b/dshow_base/seekpt.h
@@ -1,30 +1,30 @@
-//------------------------------------------------------------------------------
-// File: SeekPT.h
-//
-// Desc: DirectShow base classes.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __seekpt_h__
-#define __seekpt_h__
-
-
-class CSeekingPassThru : public ISeekingPassThru, public CUnknown
-{
-public:
- static CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
- CSeekingPassThru(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr);
- ~CSeekingPassThru();
-
- DECLARE_IUNKNOWN;
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
-
- STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin);
-
-private:
- CPosPassThru *m_pPosPassThru;
-};
-
-#endif
+//------------------------------------------------------------------------------
+// File: SeekPT.h
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __seekpt_h__
+#define __seekpt_h__
+
+
+class CSeekingPassThru : public ISeekingPassThru, public CUnknown
+{
+public:
+ static CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);
+ CSeekingPassThru(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);
+ ~CSeekingPassThru();
+
+ DECLARE_IUNKNOWN;
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
+
+ STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin);
+
+private:
+ CPosPassThru *m_pPosPassThru;
+};
+
+#endif
diff --git a/dshow_base/source.cpp b/dshow_base/source.cpp
new file mode 100644
index 0000000..ef7795c
--- /dev/null
+++ b/dshow_base/source.cpp
@@ -0,0 +1,522 @@
+//------------------------------------------------------------------------------
+// File: Source.cpp
+//
+// Desc: DirectShow base classes - implements CSource, which is a Quartz
+// source filter 'template.'
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+// Locking Strategy.
+//
+// Hold the filter critical section (m_pFilter->pStateLock()) to serialise
+// access to functions. Note that, in general, this lock may be held
+// by a function when the worker thread may want to hold it. Therefore
+// if you wish to access shared state from the worker thread you will
+// need to add another critical section object. The execption is during
+// the threads processing loop, when it is safe to get the filter critical
+// section from within FillBuffer().
+
+#include
+
+
+//
+// CSource::Constructor
+//
+// Initialise the pin count for the filter. The user will create the pins in
+// the derived class.
+CSource::CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid)
+ : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
+ m_iPins(0),
+ m_paStreams(NULL)
+{
+}
+
+CSource::CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr)
+ : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
+ m_iPins(0),
+ m_paStreams(NULL)
+{
+ UNREFERENCED_PARAMETER(phr);
+}
+
+#ifdef UNICODE
+CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid)
+ : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
+ m_iPins(0),
+ m_paStreams(NULL)
+{
+}
+
+CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr)
+ : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
+ m_iPins(0),
+ m_paStreams(NULL)
+{
+ UNREFERENCED_PARAMETER(phr);
+}
+#endif
+
+//
+// CSource::Destructor
+//
+CSource::~CSource()
+{
+ /* Free our pins and pin array */
+ while (m_iPins != 0) {
+ // deleting the pins causes them to be removed from the array...
+ delete m_paStreams[m_iPins - 1];
+ }
+
+ ASSERT(m_paStreams == NULL);
+}
+
+
+//
+// Add a new pin
+//
+HRESULT CSource::AddPin(__in CSourceStream *pStream)
+{
+ CAutoLock lock(&m_cStateLock);
+
+ /* Allocate space for this pin and the old ones */
+ CSourceStream **paStreams = new CSourceStream *[m_iPins + 1];
+ if (paStreams == NULL) {
+ return E_OUTOFMEMORY;
+ }
+ if (m_paStreams != NULL) {
+ CopyMemory((PVOID)paStreams, (PVOID)m_paStreams,
+ m_iPins * sizeof(m_paStreams[0]));
+ paStreams[m_iPins] = pStream;
+ delete [] m_paStreams;
+ }
+ m_paStreams = paStreams;
+ m_paStreams[m_iPins] = pStream;
+ m_iPins++;
+ return S_OK;
+}
+
+//
+// Remove a pin - pStream is NOT deleted
+//
+HRESULT CSource::RemovePin(__in CSourceStream *pStream)
+{
+ int i;
+ for (i = 0; i < m_iPins; i++) {
+ if (m_paStreams[i] == pStream) {
+ if (m_iPins == 1) {
+ delete [] m_paStreams;
+ m_paStreams = NULL;
+ } else {
+ /* no need to reallocate */
+ while (++i < m_iPins)
+ m_paStreams[i - 1] = m_paStreams[i];
+ }
+ m_iPins--;
+ return S_OK;
+ }
+ }
+ return S_FALSE;
+}
+
+//
+// FindPin
+//
+// Set *ppPin to the IPin* that has the id Id.
+// or to NULL if the Id cannot be matched.
+STDMETHODIMP CSource::FindPin(LPCWSTR Id, __deref_out IPin **ppPin)
+{
+ CheckPointer(ppPin,E_POINTER);
+ ValidateReadWritePtr(ppPin,sizeof(IPin *));
+ // The -1 undoes the +1 in QueryId and ensures that totally invalid
+ // strings (for which WstrToInt delivers 0) give a deliver a NULL pin.
+ int i = WstrToInt(Id) -1;
+ *ppPin = GetPin(i);
+ if (*ppPin!=NULL){
+ (*ppPin)->AddRef();
+ return NOERROR;
+ } else {
+ return VFW_E_NOT_FOUND;
+ }
+}
+
+//
+// FindPinNumber
+//
+// return the number of the pin with this IPin* or -1 if none
+int CSource::FindPinNumber(__in IPin *iPin) {
+ int i;
+ for (i=0; in && n>=0 it follows that m_iPins>0
+ // which is what used to be checked (i.e. checking that we have a pin)
+ if ((n >= 0) && (n < m_iPins)) {
+
+ ASSERT(m_paStreams[n]);
+ return m_paStreams[n];
+ }
+ return NULL;
+}
+
+
+//
+
+
+// *
+// * --- CSourceStream ----
+// *
+
+//
+// Set Id to point to a CoTaskMemAlloc'd
+STDMETHODIMP CSourceStream::QueryId(__deref_out LPWSTR *Id) {
+ CheckPointer(Id,E_POINTER);
+ ValidateReadWritePtr(Id,sizeof(LPWSTR));
+
+ // We give the pins id's which are 1,2,...
+ // FindPinNumber returns -1 for an invalid pin
+ int i = 1+ m_pFilter->FindPinNumber(this);
+ if (i<1) return VFW_E_NOT_FOUND;
+ *Id = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * 12);
+ if (*Id==NULL) {
+ return E_OUTOFMEMORY;
+ }
+ IntToWstr(i, *Id);
+ return NOERROR;
+}
+
+
+
+//
+// CSourceStream::Constructor
+//
+// increments the number of pins present on the filter
+CSourceStream::CSourceStream(
+ __in_opt LPCTSTR pObjectName,
+ __inout HRESULT *phr,
+ __inout CSource *ps,
+ __in_opt LPCWSTR pPinName)
+ : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
+ m_pFilter(ps) {
+
+ *phr = m_pFilter->AddPin(this);
+}
+
+#ifdef UNICODE
+CSourceStream::CSourceStream(
+ __in_opt LPCSTR pObjectName,
+ __inout HRESULT *phr,
+ __inout CSource *ps,
+ __in_opt LPCWSTR pPinName)
+ : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
+ m_pFilter(ps) {
+
+ *phr = m_pFilter->AddPin(this);
+}
+#endif
+//
+// CSourceStream::Destructor
+//
+// Decrements the number of pins on this filter
+CSourceStream::~CSourceStream(void) {
+
+ m_pFilter->RemovePin(this);
+}
+
+
+//
+// CheckMediaType
+//
+// Do we support this type? Provides the default support for 1 type.
+HRESULT CSourceStream::CheckMediaType(const CMediaType *pMediaType) {
+
+ CAutoLock lock(m_pFilter->pStateLock());
+
+ CMediaType mt;
+ GetMediaType(&mt);
+
+ if (mt == *pMediaType) {
+ return NOERROR;
+ }
+
+ return E_FAIL;
+}
+
+
+//
+// GetMediaType/3
+//
+// By default we support only one type
+// iPosition indexes are 0-n
+HRESULT CSourceStream::GetMediaType(int iPosition, __inout CMediaType *pMediaType) {
+
+ CAutoLock lock(m_pFilter->pStateLock());
+
+ if (iPosition<0) {
+ return E_INVALIDARG;
+ }
+ if (iPosition>0) {
+ return VFW_S_NO_MORE_ITEMS;
+ }
+ return GetMediaType(pMediaType);
+}
+
+
+//
+// Active
+//
+// The pin is active - start up the worker thread
+HRESULT CSourceStream::Active(void) {
+
+ CAutoLock lock(m_pFilter->pStateLock());
+
+ HRESULT hr;
+
+ if (m_pFilter->IsActive()) {
+ return S_FALSE; // succeeded, but did not allocate resources (they already exist...)
+ }
+
+ // do nothing if not connected - its ok not to connect to
+ // all pins of a source filter
+ if (!IsConnected()) {
+ return NOERROR;
+ }
+
+ hr = CBaseOutputPin::Active();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ ASSERT(!ThreadExists());
+
+ // start the thread
+ if (!Create()) {
+ return E_FAIL;
+ }
+
+ // Tell thread to initialize. If OnThreadCreate Fails, so does this.
+ hr = Init();
+ if (FAILED(hr))
+ return hr;
+
+ return Pause();
+}
+
+
+//
+// Inactive
+//
+// Pin is inactive - shut down the worker thread
+// Waits for the worker to exit before returning.
+HRESULT CSourceStream::Inactive(void) {
+
+ CAutoLock lock(m_pFilter->pStateLock());
+
+ HRESULT hr;
+
+ // do nothing if not connected - its ok not to connect to
+ // all pins of a source filter
+ if (!IsConnected()) {
+ return NOERROR;
+ }
+
+ // !!! need to do this before trying to stop the thread, because
+ // we may be stuck waiting for our own allocator!!!
+
+ hr = CBaseOutputPin::Inactive(); // call this first to Decommit the allocator
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ if (ThreadExists()) {
+ hr = Stop();
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = Exit();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ Close(); // Wait for the thread to exit, then tidy up.
+ }
+
+ // hr = CBaseOutputPin::Inactive(); // call this first to Decommit the allocator
+ //if (FAILED(hr)) {
+ // return hr;
+ //}
+
+ return NOERROR;
+}
+
+
+//
+// ThreadProc
+//
+// When this returns the thread exits
+// Return codes > 0 indicate an error occured
+DWORD CSourceStream::ThreadProc(void) {
+
+ HRESULT hr; // the return code from calls
+ Command com;
+
+ do {
+ com = GetRequest();
+ if (com != CMD_INIT) {
+ DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command")));
+ Reply((DWORD) E_UNEXPECTED);
+ }
+ } while (com != CMD_INIT);
+
+ DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread initializing")));
+
+ hr = OnThreadCreate(); // perform set up tasks
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));
+ OnThreadDestroy();
+ Reply(hr); // send failed return code from OnThreadCreate
+ return 1;
+ }
+
+ // Initialisation suceeded
+ Reply(NOERROR);
+
+ Command cmd;
+ do {
+ cmd = GetRequest();
+
+ switch (cmd) {
+
+ case CMD_EXIT:
+ Reply(NOERROR);
+ break;
+
+ case CMD_RUN:
+ DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));
+ // !!! fall through???
+
+ case CMD_PAUSE:
+ Reply(NOERROR);
+ DoBufferProcessingLoop();
+ break;
+
+ case CMD_STOP:
+ Reply(NOERROR);
+ break;
+
+ default:
+ DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd));
+ Reply((DWORD) E_NOTIMPL);
+ break;
+ }
+ } while (cmd != CMD_EXIT);
+
+ hr = OnThreadDestroy(); // tidy up.
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));
+ return 1;
+ }
+
+ DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting")));
+ return 0;
+}
+
+
+//
+// DoBufferProcessingLoop
+//
+// Grabs a buffer and calls the users processing function.
+// Overridable, so that different delivery styles can be catered for.
+HRESULT CSourceStream::DoBufferProcessingLoop(void) {
+
+ Command com;
+
+ OnThreadStartPlay();
+
+ do {
+ while (!CheckRequest(&com)) {
+
+ IMediaSample *pSample;
+
+ HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);
+ if (FAILED(hr)) {
+ Sleep(1);
+ continue; // go round again. Perhaps the error will go away
+ // or the allocator is decommited & we will be asked to
+ // exit soon.
+ }
+
+ // Virtual function user will override.
+ hr = FillBuffer(pSample);
+
+ if (hr == S_OK) {
+ hr = Deliver(pSample);
+ pSample->Release();
+
+ // downstream filter returns S_FALSE if it wants us to
+ // stop or an error if it's reporting an error.
+ if(hr != S_OK)
+ {
+ DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));
+ return S_OK;
+ }
+
+ } else if (hr == S_FALSE) {
+ // derived class wants us to stop pushing data
+ pSample->Release();
+ DeliverEndOfStream();
+ return S_OK;
+ } else {
+ // derived class encountered an error
+ pSample->Release();
+ DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));
+ DeliverEndOfStream();
+ m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
+ return hr;
+ }
+
+ // all paths release the sample
+ }
+
+ // For all commands sent to us there must be a Reply call!
+
+ if (com == CMD_RUN || com == CMD_PAUSE) {
+ Reply(NOERROR);
+ } else if (com != CMD_STOP) {
+ Reply((DWORD) E_UNEXPECTED);
+ DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));
+ }
+ } while (com != CMD_STOP);
+
+ return S_FALSE;
+}
+
diff --git a/dshow_base/source.h b/dshow_base/source.h
index a5f0aeb..528d5bc 100644
--- a/dshow_base/source.h
+++ b/dshow_base/source.h
@@ -1,172 +1,172 @@
-//------------------------------------------------------------------------------
-// File: Source.h
-//
-// Desc: DirectShow base classes - defines classes to simplify creation of
-// ActiveX source filters that support continuous generation of data.
-// No support is provided for IMediaControl or IMediaPosition.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-//
-// Derive your source filter from CSource.
-// During construction either:
-// Create some CSourceStream objects to manage your pins
-// Provide the user with a means of doing so eg, an IPersistFile interface.
-//
-// CSource provides:
-// IBaseFilter interface management
-// IMediaFilter interface management, via CBaseFilter
-// Pin counting for CBaseFilter
-//
-// Derive a class from CSourceStream to manage your output pin types
-// Implement GetMediaType/1 to return the type you support. If you support multiple
-// types then overide GetMediaType/3, CheckMediaType and GetMediaTypeCount.
-// Implement Fillbuffer() to put data into one buffer.
-//
-// CSourceStream provides:
-// IPin management via CBaseOutputPin
-// Worker thread management
-
-#ifndef __CSOURCE__
-#define __CSOURCE__
-
-class CSourceStream; // The class that will handle each pin
-
-
-//
-// CSource
-//
-// Override construction to provide a means of creating
-// CSourceStream derived objects - ie a way of creating pins.
-class CSource : public CBaseFilter {
-public:
-
- CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr);
- CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid);
-#ifdef UNICODE
- CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr);
- CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid);
-#endif
- ~CSource();
-
- int GetPinCount(void);
- CBasePin *GetPin(int n);
-
- // -- Utilities --
-
- CCritSec* pStateLock(void) { return &m_cStateLock; } // provide our critical section
-
- HRESULT AddPin(CSourceStream *);
- HRESULT RemovePin(CSourceStream *);
-
- STDMETHODIMP FindPin(
- LPCWSTR Id,
- IPin ** ppPin
- );
-
- int FindPinNumber(IPin *iPin);
-
-protected:
-
- int m_iPins; // The number of pins on this filter. Updated by CSourceStream
- // constructors & destructors.
- CSourceStream **m_paStreams; // the pins on this filter.
-
- CCritSec m_cStateLock; // Lock this to serialize function accesses to the filter state
-
-};
-
-
-//
-// CSourceStream
-//
-// Use this class to manage a stream of data that comes from a
-// pin.
-// Uses a worker thread to put data on the pin.
-class CSourceStream : public CAMThread, public CBaseOutputPin {
-public:
-
- CSourceStream(TCHAR *pObjectName,
- HRESULT *phr,
- CSource *pms,
- LPCWSTR pName);
-#ifdef UNICODE
- CSourceStream(CHAR *pObjectName,
- HRESULT *phr,
- CSource *pms,
- LPCWSTR pName);
-#endif
- virtual ~CSourceStream(void); // virtual destructor ensures derived class destructors are called too.
-
-protected:
-
- CSource *m_pFilter; // The parent of this stream
-
- // *
- // * Data Source
- // *
- // * The following three functions: FillBuffer, OnThreadCreate/Destroy, are
- // * called from within the ThreadProc. They are used in the creation of
- // * the media samples this pin will provide
- // *
-
- // Override this to provide the worker thread a means
- // of processing a buffer
- virtual HRESULT FillBuffer(IMediaSample *pSamp) PURE;
-
- // Called as the thread is created/destroyed - use to perform
- // jobs such as start/stop streaming mode
- // If OnThreadCreate returns an error the thread will exit.
- virtual HRESULT OnThreadCreate(void) {return NOERROR;};
- virtual HRESULT OnThreadDestroy(void) {return NOERROR;};
- virtual HRESULT OnThreadStartPlay(void) {return NOERROR;};
-
- // *
- // * Worker Thread
- // *
-
- HRESULT Active(void); // Starts up the worker thread
- HRESULT Inactive(void); // Exits the worker thread.
-
-public:
- // thread commands
- enum Command {CMD_INIT, CMD_PAUSE, CMD_RUN, CMD_STOP, CMD_EXIT};
- HRESULT Init(void) { return CallWorker(CMD_INIT); }
- HRESULT Exit(void) { return CallWorker(CMD_EXIT); }
- HRESULT Run(void) { return CallWorker(CMD_RUN); }
- HRESULT Pause(void) { return CallWorker(CMD_PAUSE); }
- HRESULT Stop(void) { return CallWorker(CMD_STOP); }
-
-protected:
- Command GetRequest(void) { return (Command) CAMThread::GetRequest(); }
- BOOL CheckRequest(Command *pCom) { return CAMThread::CheckRequest( (DWORD *) pCom); }
-
- // override these if you want to add thread commands
- virtual DWORD ThreadProc(void); // the thread function
-
- virtual HRESULT DoBufferProcessingLoop(void); // the loop executed whilst running
-
-
- // *
- // * AM_MEDIA_TYPE support
- // *
-
- // If you support more than one media type then override these 2 functions
- virtual HRESULT CheckMediaType(const CMediaType *pMediaType);
- virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType); // List pos. 0-n
-
- // If you support only one type then override this fn.
- // This will only be called by the default implementations
- // of CheckMediaType and GetMediaType(int, CMediaType*)
- // You must override this fn. or the above 2!
- virtual HRESULT GetMediaType(CMediaType *pMediaType) {return E_UNEXPECTED;}
-
- STDMETHODIMP QueryId(
- LPWSTR * Id
- );
-};
-
-#endif // __CSOURCE__
-
+//------------------------------------------------------------------------------
+// File: Source.h
+//
+// Desc: DirectShow base classes - defines classes to simplify creation of
+// ActiveX source filters that support continuous generation of data.
+// No support is provided for IMediaControl or IMediaPosition.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+//
+// Derive your source filter from CSource.
+// During construction either:
+// Create some CSourceStream objects to manage your pins
+// Provide the user with a means of doing so eg, an IPersistFile interface.
+//
+// CSource provides:
+// IBaseFilter interface management
+// IMediaFilter interface management, via CBaseFilter
+// Pin counting for CBaseFilter
+//
+// Derive a class from CSourceStream to manage your output pin types
+// Implement GetMediaType/1 to return the type you support. If you support multiple
+// types then overide GetMediaType/3, CheckMediaType and GetMediaTypeCount.
+// Implement Fillbuffer() to put data into one buffer.
+//
+// CSourceStream provides:
+// IPin management via CBaseOutputPin
+// Worker thread management
+
+#ifndef __CSOURCE__
+#define __CSOURCE__
+
+class CSourceStream; // The class that will handle each pin
+
+
+//
+// CSource
+//
+// Override construction to provide a means of creating
+// CSourceStream derived objects - ie a way of creating pins.
+class CSource : public CBaseFilter {
+public:
+
+ CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr);
+ CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid);
+#ifdef UNICODE
+ CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr);
+ CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid);
+#endif
+ ~CSource();
+
+ int GetPinCount(void);
+ CBasePin *GetPin(int n);
+
+ // -- Utilities --
+
+ CCritSec* pStateLock(void) { return &m_cStateLock; } // provide our critical section
+
+ HRESULT AddPin(__in CSourceStream *);
+ HRESULT RemovePin(__in CSourceStream *);
+
+ STDMETHODIMP FindPin(
+ LPCWSTR Id,
+ __deref_out IPin ** ppPin
+ );
+
+ int FindPinNumber(__in IPin *iPin);
+
+protected:
+
+ int m_iPins; // The number of pins on this filter. Updated by CSourceStream
+ // constructors & destructors.
+ CSourceStream **m_paStreams; // the pins on this filter.
+
+ CCritSec m_cStateLock; // Lock this to serialize function accesses to the filter state
+
+};
+
+
+//
+// CSourceStream
+//
+// Use this class to manage a stream of data that comes from a
+// pin.
+// Uses a worker thread to put data on the pin.
+class CSourceStream : public CAMThread, public CBaseOutputPin {
+public:
+
+ CSourceStream(__in_opt LPCTSTR pObjectName,
+ __inout HRESULT *phr,
+ __inout CSource *pms,
+ __in_opt LPCWSTR pName);
+#ifdef UNICODE
+ CSourceStream(__in_opt LPCSTR pObjectName,
+ __inout HRESULT *phr,
+ __inout CSource *pms,
+ __in_opt LPCWSTR pName);
+#endif
+ virtual ~CSourceStream(void); // virtual destructor ensures derived class destructors are called too.
+
+protected:
+
+ CSource *m_pFilter; // The parent of this stream
+
+ // *
+ // * Data Source
+ // *
+ // * The following three functions: FillBuffer, OnThreadCreate/Destroy, are
+ // * called from within the ThreadProc. They are used in the creation of
+ // * the media samples this pin will provide
+ // *
+
+ // Override this to provide the worker thread a means
+ // of processing a buffer
+ virtual HRESULT FillBuffer(IMediaSample *pSamp) PURE;
+
+ // Called as the thread is created/destroyed - use to perform
+ // jobs such as start/stop streaming mode
+ // If OnThreadCreate returns an error the thread will exit.
+ virtual HRESULT OnThreadCreate(void) {return NOERROR;};
+ virtual HRESULT OnThreadDestroy(void) {return NOERROR;};
+ virtual HRESULT OnThreadStartPlay(void) {return NOERROR;};
+
+ // *
+ // * Worker Thread
+ // *
+
+ HRESULT Active(void); // Starts up the worker thread
+ HRESULT Inactive(void); // Exits the worker thread.
+
+public:
+ // thread commands
+ enum Command {CMD_INIT, CMD_PAUSE, CMD_RUN, CMD_STOP, CMD_EXIT};
+ HRESULT Init(void) { return CallWorker(CMD_INIT); }
+ HRESULT Exit(void) { return CallWorker(CMD_EXIT); }
+ HRESULT Run(void) { return CallWorker(CMD_RUN); }
+ HRESULT Pause(void) { return CallWorker(CMD_PAUSE); }
+ HRESULT Stop(void) { return CallWorker(CMD_STOP); }
+
+protected:
+ Command GetRequest(void) { return (Command) CAMThread::GetRequest(); }
+ BOOL CheckRequest(Command *pCom) { return CAMThread::CheckRequest( (DWORD *) pCom); }
+
+ // override these if you want to add thread commands
+ virtual DWORD ThreadProc(void); // the thread function
+
+ virtual HRESULT DoBufferProcessingLoop(void); // the loop executed whilst running
+
+
+ // *
+ // * AM_MEDIA_TYPE support
+ // *
+
+ // If you support more than one media type then override these 2 functions
+ virtual HRESULT CheckMediaType(const CMediaType *pMediaType);
+ virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType); // List pos. 0-n
+
+ // If you support only one type then override this fn.
+ // This will only be called by the default implementations
+ // of CheckMediaType and GetMediaType(int, CMediaType*)
+ // You must override this fn. or the above 2!
+ virtual HRESULT GetMediaType(__inout CMediaType *pMediaType) {return E_UNEXPECTED;}
+
+ STDMETHODIMP QueryId(
+ __deref_out LPWSTR * Id
+ );
+};
+
+#endif // __CSOURCE__
+
diff --git a/dshow_base/streams.h b/dshow_base/streams.h
index 1ca0d0b..1926321 100644
--- a/dshow_base/streams.h
+++ b/dshow_base/streams.h
@@ -1,191 +1,202 @@
-//------------------------------------------------------------------------------
-// File: Streams.h
-//
-// Desc: DirectShow base classes - defines overall streams architecture.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __STREAMS__
-#define __STREAMS__
-
-#ifdef _MSC_VER
-// disable some level-4 warnings, use #pragma warning(enable:###) to re-enable
-#pragma warning(disable:4100) // warning C4100: unreferenced formal parameter
-#pragma warning(disable:4127) // warning C4127: conditional expression is constant
-#pragma warning(disable:4189) // warning C4189: local variable is initialized but not referenced
-#pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
-#pragma warning(disable:4511) // warning C4511: copy constructor could not be generated
-#pragma warning(disable:4512) // warning C4512: assignment operator could not be generated
-#pragma warning(disable:4514) // warning C4514: unreferenced inline function has been removed
-#pragma warning(disable:4710) // warning C4710: 'function' not inlined
-
-#if _MSC_VER>=1100
-#define AM_NOVTABLE __declspec(novtable)
-#else
-#define AM_NOVTABLE
-#endif
-#endif // MSC_VER
-
-// Because of differences between Visual C++ and older Microsoft SDKs,
-// you may have defined _DEBUG without defining DEBUG. This logic
-// ensures that both will be set if Visual C++ sets _DEBUG.
-#ifdef _DEBUG
-#ifndef DEBUG
-#define DEBUG
-#endif
-#endif
-
-
-#include
-#include
-#include
-#include
-
-// Disable warning message for C4201 - use of nameless struct/union
-// Otherwise, strmif.h will generate warnings for Win32 debug builds
-#pragma warning( disable : 4201 )
-
-#include
-
-#ifndef NUMELMS
- #define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
-#endif
-
-///////////////////////////////////////////////////////////////////////////
-// The following definitions come from the Platform SDK and are required if
-// the applicaiton is being compiled with the headers from Visual C++ 6.0.
-///////////////////////////////////////////////////////////////////////////
-#ifndef InterlockedExchangePointer
- #define InterlockedExchangePointer(Target, Value) \
- (PVOID)InterlockedExchange((PLONG)(Target), (LONG)(Value))
-#endif
-
-#ifndef _WAVEFORMATEXTENSIBLE_
-#define _WAVEFORMATEXTENSIBLE_
-typedef struct {
- WAVEFORMATEX Format;
- union {
- WORD wValidBitsPerSample; /* bits of precision */
- WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
- WORD wReserved; /* If neither applies, set to zero. */
- } Samples;
- DWORD dwChannelMask; /* which channels are */
- /* present in stream */
- GUID SubFormat;
-} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
-#endif // !_WAVEFORMATEXTENSIBLE_
-
-#if !defined(WAVE_FORMAT_EXTENSIBLE)
-#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
-#endif // !defined(WAVE_FORMAT_EXTENSIBLE)
-
-#ifndef GetWindowLongPtr
- #define GetWindowLongPtrA GetWindowLongA
- #define GetWindowLongPtrW GetWindowLongW
- #ifdef UNICODE
- #define GetWindowLongPtr GetWindowLongPtrW
- #else
- #define GetWindowLongPtr GetWindowLongPtrA
- #endif // !UNICODE
-#endif // !GetWindowLongPtr
-
-#ifndef SetWindowLongPtr
- #define SetWindowLongPtrA SetWindowLongA
- #define SetWindowLongPtrW SetWindowLongW
- #ifdef UNICODE
- #define SetWindowLongPtr SetWindowLongPtrW
- #else
- #define SetWindowLongPtr SetWindowLongPtrA
- #endif // !UNICODE
-#endif // !SetWindowLongPtr
-
-#ifndef GWLP_WNDPROC
- #define GWLP_WNDPROC (-4)
-#endif
-#ifndef GWLP_HINSTANCE
- #define GWLP_HINSTANCE (-6)
-#endif
-#ifndef GWLP_HWNDPARENT
- #define GWLP_HWNDPARENT (-8)
-#endif
-#ifndef GWLP_USERDATA
- #define GWLP_USERDATA (-21)
-#endif
-#ifndef GWLP_ID
- #define GWLP_ID (-12)
-#endif
-#ifndef DWLP_MSGRESULT
- #define DWLP_MSGRESULT 0
-#endif
-#ifndef DWLP_DLGPROC
- #define DWLP_DLGPROC DWLP_MSGRESULT + sizeof(LRESULT)
-#endif
-#ifndef DWLP_USER
- #define DWLP_USER DWLP_DLGPROC + sizeof(DLGPROC)
-#endif
-///////////////////////////////////////////////////////////////////////////
-// End Platform SDK definitions
-///////////////////////////////////////////////////////////////////////////
-
-
-#pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
-#include // Generated IDL header file for streams interfaces
-
-#include // Helper class for REFERENCE_TIME management
-#include // Debug support for logging and ASSERTs
-#include // ActiveMovie video interfaces and definitions
-//include amaudio.h explicitly if you need it. it requires the DirectX SDK.
-//#include // ActiveMovie audio interfaces and definitions
-#include // General helper classes for threads etc
-#include // Base COM classes to support IUnknown
-#include // Filter registration support functions
-#include // Performance measurement
-#include // Light weight com function prototypes
-
-#include // Simple cache container class
-#include // Non MFC generic list class
-#include // CMsgThread
-#include // Helper class for managing media types
-#include // conversions between FOURCCs and GUIDs
-#include // generated from control.odl
-#include // control interface utility classes
-#include // event code definitions
-#include // Main streams architecture class hierachy
-#include // Generic transform filter
-#include // Generic transform-in-place filter
-#include // declaration of type GUIDs and well-known clsids
-#include // Generic source filter
-#include // Output pin queueing
-#include // HRESULT status and error definitions
-#include // Base class for writing ActiveX renderers
-#include // Helps with filters that manage windows
-#include // Implements the IVideoWindow interface
-#include // Specifically video related classes
-#include // Base clock class
-#include // System clock
-#include // IPersistStream helper class
-#include // Video Transform Filter base class
-#include
-#include // Base property page class
-#include // IAMStreamControl support
-#include // External device control interface defines
-#include // audio filter device error event codes
-
-#include
-
-#define NO_SHLWAPI_STRFCNS
-#include
-#include
-
-#ifndef NUMELMS
- #define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
-#endif
-
-#else
- #ifdef DEBUG
- #pragma message("STREAMS.H included TWICE")
- #endif
-#endif // __STREAMS__
-
+//------------------------------------------------------------------------------
+// File: Streams.h
+//
+// Desc: DirectShow base classes - defines overall streams architecture.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __STREAMS__
+#define __STREAMS__
+
+#ifdef _MSC_VER
+// disable some level-4 warnings, use #pragma warning(enable:###) to re-enable
+#pragma warning(disable:4100) // warning C4100: unreferenced formal parameter
+#pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
+#pragma warning(disable:4511) // warning C4511: copy constructor could not be generated
+#pragma warning(disable:4512) // warning C4512: assignment operator could not be generated
+#pragma warning(disable:4514) // warning C4514: "unreferenced inline function has been removed"
+
+#if _MSC_VER>=1100
+#define AM_NOVTABLE __declspec(novtable)
+#else
+#define AM_NOVTABLE
+#endif
+#endif // MSC_VER
+
+
+// Because of differences between Visual C++ and older Microsoft SDKs,
+// you may have defined _DEBUG without defining DEBUG. This logic
+// ensures that both will be set if Visual C++ sets _DEBUG.
+#ifdef _DEBUG
+#ifndef DEBUG
+#define DEBUG
+#endif
+#endif
+
+
+#include
+#include
+#include
+#include
+#include
+
+
+#ifndef NUMELMS
+#if _WIN32_WINNT < 0x0600
+ #define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
+#else
+ #define NUMELMS(aa) ARRAYSIZE(aa)
+#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////
+// The following definitions come from the Platform SDK and are required if
+// the applicaiton is being compiled with the headers from Visual C++ 6.0.
+/////////////////////////////////////////////////// ////////////////////////
+#ifndef InterlockedExchangePointer
+ #define InterlockedExchangePointer(Target, Value) \
+ (PVOID)InterlockedExchange((PLONG)(Target), (LONG)(Value))
+#endif
+
+#ifndef _WAVEFORMATEXTENSIBLE_
+#define _WAVEFORMATEXTENSIBLE_
+typedef struct {
+ WAVEFORMATEX Format;
+ union {
+ WORD wValidBitsPerSample; /* bits of precision */
+ WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
+ WORD wReserved; /* If neither applies, set to zero. */
+ } Samples;
+ DWORD dwChannelMask; /* which channels are */
+ /* present in stream */
+ GUID SubFormat;
+} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
+#endif // !_WAVEFORMATEXTENSIBLE_
+
+#if !defined(WAVE_FORMAT_EXTENSIBLE)
+#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
+#endif // !defined(WAVE_FORMAT_EXTENSIBLE)
+
+#ifndef GetWindowLongPtr
+ #define GetWindowLongPtrA GetWindowLongA
+ #define GetWindowLongPtrW GetWindowLongW
+ #ifdef UNICODE
+ #define GetWindowLongPtr GetWindowLongPtrW
+ #else
+ #define GetWindowLongPtr GetWindowLongPtrA
+ #endif // !UNICODE
+#endif // !GetWindowLongPtr
+
+#ifndef SetWindowLongPtr
+ #define SetWindowLongPtrA SetWindowLongA
+ #define SetWindowLongPtrW SetWindowLongW
+ #ifdef UNICODE
+ #define SetWindowLongPtr SetWindowLongPtrW
+ #else
+ #define SetWindowLongPtr SetWindowLongPtrA
+ #endif // !UNICODE
+#endif // !SetWindowLongPtr
+
+#ifndef GWLP_WNDPROC
+ #define GWLP_WNDPROC (-4)
+#endif
+#ifndef GWLP_HINSTANCE
+ #define GWLP_HINSTANCE (-6)
+#endif
+#ifndef GWLP_HWNDPARENT
+ #define GWLP_HWNDPARENT (-8)
+#endif
+#ifndef GWLP_USERDATA
+ #define GWLP_USERDATA (-21)
+#endif
+#ifndef GWLP_ID
+ #define GWLP_ID (-12)
+#endif
+#ifndef DWLP_MSGRESULT
+ #define DWLP_MSGRESULT 0
+#endif
+#ifndef DWLP_DLGPROC
+ #define DWLP_DLGPROC DWLP_MSGRESULT + sizeof(LRESULT)
+#endif
+#ifndef DWLP_USER
+ #define DWLP_USER DWLP_DLGPROC + sizeof(DLGPROC)
+#endif
+
+
+#pragma warning(push)
+#pragma warning(disable: 4312 4244)
+// _GetWindowLongPtr
+// Templated version of GetWindowLongPtr, to suppress spurious compiler warning.
+template
+T _GetWindowLongPtr(HWND hwnd, int nIndex)
+{
+ return (T)GetWindowLongPtr(hwnd, nIndex);
+}
+
+// _SetWindowLongPtr
+// Templated version of SetWindowLongPtr, to suppress spurious compiler warning.
+template
+LONG_PTR _SetWindowLongPtr(HWND hwnd, int nIndex, T p)
+{
+ return SetWindowLongPtr(hwnd, nIndex, (LONG_PTR)p);
+}
+#pragma warning(pop)
+
+///////////////////////////////////////////////////////////////////////////
+// End Platform SDK definitions
+///////////////////////////////////////////////////////////////////////////
+
+
+#include // Generated IDL header file for streams interfaces
+#include // required by amvideo.h
+
+#include // Helper class for REFERENCE_TIME management
+#include // Debug support for logging and ASSERTs
+#include // ActiveMovie video interfaces and definitions
+//include amaudio.h explicitly if you need it. it requires the DX SDK.
+//#include // ActiveMovie audio interfaces and definitions
+#include // General helper classes for threads etc
+#include // Base COM classes to support IUnknown
+#include // Filter registration support functions
+#include // Performance measurement
+#include // Light weight com function prototypes
+
+#include // Simple cache container class
+#include // Non MFC generic list class
+#include // CMsgThread
+#include // Helper class for managing media types
+#include // conversions between FOURCCs and GUIDs
+#include // generated from control.odl
+#include // control interface utility classes
+#include // event code definitions
+#include // Main streams architecture class hierachy
+#include // Generic transform filter
+#include // Generic transform-in-place filter
+#include // declaration of type GUIDs and well-known clsids
+#include // Generic source filter
+#include // Output pin queueing
+#include // HRESULT status and error definitions
+#include // Base class for writing ActiveX renderers
+#include // Helps with filters that manage windows
+#include // Implements the IVideoWindow interface
+#include // Specifically video related classes
+#include // Base clock class
+#include // System clock
+#include // IPersistStream helper class
+#include // Video Transform Filter base class
+#include
+#include // Base property page class
+#include // IAMStreamControl support
+#include // External device control interface defines
+#include // audio filter device error event codes
+
+
+
+#else
+ #ifdef DEBUG
+ #pragma message("STREAMS.H included TWICE")
+ #endif
+#endif // __STREAMS__
+
diff --git a/dshow_base/strmbase_d.lib b/dshow_base/strmbase_d.lib
deleted file mode 100644
index 816385c..0000000
Binary files a/dshow_base/strmbase_d.lib and /dev/null differ
diff --git a/dshow_base/strmbase_r.lib b/dshow_base/strmbase_r.lib
deleted file mode 100644
index 7c3925c..0000000
Binary files a/dshow_base/strmbase_r.lib and /dev/null differ
diff --git a/dshow_base/strmctl.cpp b/dshow_base/strmctl.cpp
new file mode 100644
index 0000000..b7f5952
--- /dev/null
+++ b/dshow_base/strmctl.cpp
@@ -0,0 +1,402 @@
+//------------------------------------------------------------------------------
+// File: StrmCtl.cpp
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#include
+
+CBaseStreamControl::CBaseStreamControl(__inout HRESULT *phr)
+: m_StreamState(STREAM_FLOWING)
+, m_StreamStateOnStop(STREAM_FLOWING) // means no pending stop
+, m_tStartTime(MAX_TIME)
+, m_tStopTime(MAX_TIME)
+, m_StreamEvent(FALSE, phr)
+, m_dwStartCookie(0)
+, m_dwStopCookie(0)
+, m_pRefClock(NULL)
+, m_FilterState(State_Stopped)
+, m_bIsFlushing(FALSE)
+, m_bStopSendExtra(FALSE)
+{}
+
+CBaseStreamControl::~CBaseStreamControl()
+{
+ // Make sure we release the clock.
+ SetSyncSource(NULL);
+ return;
+}
+
+
+STDMETHODIMP CBaseStreamControl::StopAt(const REFERENCE_TIME * ptStop, BOOL bSendExtra, DWORD dwCookie)
+{
+ CAutoLock lck(&m_CritSec);
+ m_bStopSendExtra = FALSE; // reset
+ m_bStopExtraSent = FALSE;
+ if (ptStop)
+ {
+ if (*ptStop == MAX_TIME)
+ {
+ DbgLog((LOG_TRACE,2,TEXT("StopAt: Cancel stop")));
+ CancelStop();
+ // If there's now a command to start in the future, we assume
+ // they want to be stopped when the graph is first run
+ if (m_FilterState == State_Stopped && m_tStartTime < MAX_TIME) {
+ m_StreamState = STREAM_DISCARDING;
+ DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
+ }
+ return NOERROR;
+ }
+ DbgLog((LOG_TRACE,2,TEXT("StopAt: %dms extra=%d"),
+ (int)(*ptStop/10000), bSendExtra));
+ // if the first command is to stop in the future, then we assume they
+ // want to be started when the graph is first run
+ if (m_FilterState == State_Stopped && m_tStartTime > *ptStop) {
+ m_StreamState = STREAM_FLOWING;
+ DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
+ }
+ m_bStopSendExtra = bSendExtra;
+ m_tStopTime = *ptStop;
+ m_dwStopCookie = dwCookie;
+ m_StreamStateOnStop = STREAM_DISCARDING;
+ }
+ else
+ {
+ DbgLog((LOG_TRACE,2,TEXT("StopAt: now")));
+ // sending an extra frame when told to stop now would mess people up
+ m_bStopSendExtra = FALSE;
+ m_tStopTime = MAX_TIME;
+ m_dwStopCookie = 0;
+ m_StreamState = STREAM_DISCARDING;
+ m_StreamStateOnStop = STREAM_FLOWING; // no pending stop
+ }
+ // we might change our mind what to do with a sample we're blocking
+ m_StreamEvent.Set();
+ return NOERROR;
+}
+
+STDMETHODIMP CBaseStreamControl::StartAt
+( const REFERENCE_TIME *ptStart, DWORD dwCookie )
+{
+ CAutoLock lck(&m_CritSec);
+ if (ptStart)
+ {
+ if (*ptStart == MAX_TIME)
+ {
+ DbgLog((LOG_TRACE,2,TEXT("StartAt: Cancel start")));
+ CancelStart();
+ // If there's now a command to stop in the future, we assume
+ // they want to be started when the graph is first run
+ if (m_FilterState == State_Stopped && m_tStopTime < MAX_TIME) {
+ DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
+ m_StreamState = STREAM_FLOWING;
+ }
+ return NOERROR;
+ }
+ DbgLog((LOG_TRACE,2,TEXT("StartAt: %dms"), (int)(*ptStart/10000)));
+ // if the first command is to start in the future, then we assume they
+ // want to be stopped when the graph is first run
+ if (m_FilterState == State_Stopped && m_tStopTime >= *ptStart) {
+ DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
+ m_StreamState = STREAM_DISCARDING;
+ }
+ m_tStartTime = *ptStart;
+ m_dwStartCookie = dwCookie;
+ // if (m_tStopTime == m_tStartTime) CancelStop();
+ }
+ else
+ {
+ DbgLog((LOG_TRACE,2,TEXT("StartAt: now")));
+ m_tStartTime = MAX_TIME;
+ m_dwStartCookie = 0;
+ m_StreamState = STREAM_FLOWING;
+ }
+ // we might change our mind what to do with a sample we're blocking
+ m_StreamEvent.Set();
+ return NOERROR;
+}
+
+// Retrieve information about current settings
+STDMETHODIMP CBaseStreamControl::GetInfo(__out AM_STREAM_INFO *pInfo)
+{
+ if (pInfo == NULL)
+ return E_POINTER;
+
+ pInfo->tStart = m_tStartTime;
+ pInfo->tStop = m_tStopTime;
+ pInfo->dwStartCookie = m_dwStartCookie;
+ pInfo->dwStopCookie = m_dwStopCookie;
+ pInfo->dwFlags = m_bStopSendExtra ? AM_STREAM_INFO_STOP_SEND_EXTRA : 0;
+ pInfo->dwFlags |= m_tStartTime == MAX_TIME ? 0 : AM_STREAM_INFO_START_DEFINED;
+ pInfo->dwFlags |= m_tStopTime == MAX_TIME ? 0 : AM_STREAM_INFO_STOP_DEFINED;
+ switch (m_StreamState) {
+ default:
+ DbgBreak("Invalid stream state");
+ case STREAM_FLOWING:
+ break;
+ case STREAM_DISCARDING:
+ pInfo->dwFlags |= AM_STREAM_INFO_DISCARDING;
+ break;
+ }
+ return S_OK;
+}
+
+
+void CBaseStreamControl::ExecuteStop()
+{
+ ASSERT(CritCheckIn(&m_CritSec));
+ m_StreamState = m_StreamStateOnStop;
+ if (m_dwStopCookie && m_pSink) {
+ DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STOPPED (%d)"),
+ m_dwStopCookie));
+ m_pSink->Notify(EC_STREAM_CONTROL_STOPPED, (LONG_PTR)this, m_dwStopCookie);
+ }
+ CancelStop(); // This will do the tidy up
+}
+
+void CBaseStreamControl::ExecuteStart()
+{
+ ASSERT(CritCheckIn(&m_CritSec));
+ m_StreamState = STREAM_FLOWING;
+ if (m_dwStartCookie) {
+ DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STARTED (%d)"),
+ m_dwStartCookie));
+ m_pSink->Notify(EC_STREAM_CONTROL_STARTED, (LONG_PTR)this, m_dwStartCookie);
+ }
+ CancelStart(); // This will do the tidy up
+}
+
+void CBaseStreamControl::CancelStop()
+{
+ ASSERT(CritCheckIn(&m_CritSec));
+ m_tStopTime = MAX_TIME;
+ m_dwStopCookie = 0;
+ m_StreamStateOnStop = STREAM_FLOWING;
+}
+
+void CBaseStreamControl::CancelStart()
+{
+ ASSERT(CritCheckIn(&m_CritSec));
+ m_tStartTime = MAX_TIME;
+ m_dwStartCookie = 0;
+}
+
+
+// This guy will return one of the three StreamControlState's. Here's what the caller
+// should do for each one:
+//
+// STREAM_FLOWING: Proceed as usual (render or pass the sample on)
+// STREAM_DISCARDING: Calculate the time 'til *pSampleStart and wait that long
+// for the event handle (GetStreamEventHandle()). If the
+// wait expires, throw the sample away. If the event
+// fires, call me back, I've changed my mind.
+// I use pSampleStart (not Stop) so that live sources don't
+// block for the duration of their samples, since the clock
+// will always read approximately pSampleStart when called
+
+
+// All through this code, you'll notice the following rules:
+// - When start and stop time are the same, it's as if start was first
+// - An event is considered inside the sample when it's >= sample start time
+// but < sample stop time
+// - if any part of the sample is supposed to be sent, we'll send the whole
+// thing since we don't break it into smaller pieces
+// - If we skip over a start or stop without doing it, we still signal the event
+// and reset ourselves in case somebody's waiting for the event, and to make
+// sure we notice that the event is past and should be forgotten
+// Here are the 19 cases that have to be handled (x=start o=stop <-->=sample):
+//
+// 1. xo<--> start then stop
+// 2. ox<--> stop then start
+// 3. x start
+// 4. o stop then start
+// 5. x<-->o start
+// 6. o<-->x stop
+// 7. o start
+// 8. x no change
+// 9. start
+// 10. stop then start
+// 11. <-->xo no change
+// 12. <-->ox no change
+// 13. x<--> start
+// 14. start
+// 15. <-->x no change
+// 16. o<--> stop
+// 17. no change
+// 18. <-->o no change
+// 19. <--> no change
+
+
+enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckSampleTimes
+( __in const REFERENCE_TIME * pSampleStart, __in const REFERENCE_TIME * pSampleStop )
+{
+ CAutoLock lck(&m_CritSec);
+
+ ASSERT(!m_bIsFlushing);
+ ASSERT(pSampleStart && pSampleStop);
+
+ // Don't ask me how I came up with the code below to handle all 19 cases
+ // - DannyMi
+
+ if (m_tStopTime >= *pSampleStart)
+ {
+ if (m_tStartTime >= *pSampleStop)
+ return m_StreamState; // cases 8 11 12 15 17 18 19
+ if (m_tStopTime < m_tStartTime)
+ ExecuteStop(); // case 10
+ ExecuteStart(); // cases 3 5 7 9 13 14
+ return m_StreamState;
+ }
+
+ if (m_tStartTime >= *pSampleStop)
+ {
+ ExecuteStop(); // cases 6 16
+ return m_StreamState;
+ }
+
+ if (m_tStartTime <= m_tStopTime)
+ {
+ ExecuteStart();
+ ExecuteStop();
+ return m_StreamState; // case 1
+ }
+ else
+ {
+ ExecuteStop();
+ ExecuteStart();
+ return m_StreamState; // cases 2 4
+ }
+}
+
+
+enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckStreamState( IMediaSample * pSample )
+{
+
+ REFERENCE_TIME rtBufferStart, rtBufferStop;
+ const BOOL bNoBufferTimes =
+ pSample == NULL ||
+ FAILED(pSample->GetTime(&rtBufferStart, &rtBufferStop));
+
+ StreamControlState state;
+ LONG lWait;
+
+ do
+ {
+ // something has to break out of the blocking
+ if (m_bIsFlushing || m_FilterState == State_Stopped)
+ return STREAM_DISCARDING;
+
+ if (bNoBufferTimes) {
+ // Can't do anything until we get a time stamp
+ state = m_StreamState;
+ break;
+ } else {
+ state = CheckSampleTimes( &rtBufferStart, &rtBufferStop );
+ if (state == STREAM_FLOWING)
+ break;
+
+ // we aren't supposed to send this, but we've been
+ // told to send one more than we were supposed to
+ // (and the stop isn't still pending and we're streaming)
+ if (m_bStopSendExtra && !m_bStopExtraSent &&
+ m_tStopTime == MAX_TIME &&
+ m_FilterState != State_Stopped) {
+ m_bStopExtraSent = TRUE;
+ DbgLog((LOG_TRACE,2,TEXT("%d sending an EXTRA frame"),
+ m_dwStopCookie));
+ state = STREAM_FLOWING;
+ break;
+ }
+ }
+
+ // We're in discarding mode
+
+ // If we've no clock, discard as fast as we can
+ if (!m_pRefClock) {
+ break;
+
+ // If we're paused, we can't discard in a timely manner because
+ // there's no such thing as stream times. We must block until
+ // we run or stop, or we'll end up throwing the whole stream away
+ // as quickly as possible
+ } else if (m_FilterState == State_Paused) {
+ lWait = INFINITE;
+
+ } else {
+ // wait until it's time for the sample until we say "discard"
+ // ("discard in a timely fashion")
+ REFERENCE_TIME rtNow;
+ EXECUTE_ASSERT(SUCCEEDED(m_pRefClock->GetTime(&rtNow)));
+ rtNow -= m_tRunStart; // Into relative ref-time
+ lWait = LONG((rtBufferStart - rtNow)/10000); // 100ns -> ms
+ if (lWait < 10) break; // Not worth waiting - discard early
+ }
+
+ } while(WaitForSingleObject(GetStreamEventHandle(), lWait) != WAIT_TIMEOUT);
+
+ return state;
+}
+
+
+void CBaseStreamControl::NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart )
+{
+ CAutoLock lck(&m_CritSec);
+
+ // or we will get confused
+ if (m_FilterState == new_state)
+ return;
+
+ switch (new_state)
+ {
+ case State_Stopped:
+
+ DbgLog((LOG_TRACE,2,TEXT("Filter is STOPPED")));
+
+ // execute any pending starts and stops in the right order,
+ // to make sure all notifications get sent, and we end up
+ // in the right state to begin next time (??? why not?)
+
+ if (m_tStartTime != MAX_TIME && m_tStopTime == MAX_TIME) {
+ ExecuteStart();
+ } else if (m_tStopTime != MAX_TIME && m_tStartTime == MAX_TIME) {
+ ExecuteStop();
+ } else if (m_tStopTime != MAX_TIME && m_tStartTime != MAX_TIME) {
+ if (m_tStartTime <= m_tStopTime) {
+ ExecuteStart();
+ ExecuteStop();
+ } else {
+ ExecuteStop();
+ ExecuteStart();
+ }
+ }
+ // always start off flowing when the graph starts streaming
+ // unless told otherwise
+ m_StreamState = STREAM_FLOWING;
+ m_FilterState = new_state;
+ break;
+
+ case State_Running:
+
+ DbgLog((LOG_TRACE,2,TEXT("Filter is RUNNING")));
+
+ m_tRunStart = tStart;
+ // fall-through
+
+ default: // case State_Paused:
+ m_FilterState = new_state;
+ }
+ // unblock!
+ m_StreamEvent.Set();
+}
+
+
+void CBaseStreamControl::Flushing(BOOL bInProgress)
+{
+ CAutoLock lck(&m_CritSec);
+ m_bIsFlushing = bInProgress;
+ m_StreamEvent.Set();
+}
diff --git a/dshow_base/strmctl.h b/dshow_base/strmctl.h
index d054777..cb2adf3 100644
--- a/dshow_base/strmctl.h
+++ b/dshow_base/strmctl.h
@@ -1,157 +1,157 @@
-//------------------------------------------------------------------------------
-// File: StrmCtl.h
-//
-// Desc: DirectShow base classes.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __strmctl_h__
-#define __strmctl_h__
-
-class CBaseStreamControl : public IAMStreamControl
-{
-public:
- // Used by the implementation
- enum StreamControlState
- { STREAM_FLOWING = 0x1000,
- STREAM_DISCARDING
- };
-
-private:
- enum StreamControlState m_StreamState; // Current stream state
- enum StreamControlState m_StreamStateOnStop; // State after next stop
- // (i.e.Blocking or Discarding)
-
- REFERENCE_TIME m_tStartTime; // MAX_TIME implies none
- REFERENCE_TIME m_tStopTime; // MAX_TIME implies none
- DWORD m_dwStartCookie; // Cookie for notification to app
- DWORD m_dwStopCookie; // Cookie for notification to app
- volatile BOOL m_bIsFlushing; // No optimization pls!
- volatile BOOL m_bStopSendExtra; // bSendExtra was set
- volatile BOOL m_bStopExtraSent; // the extra one was sent
-
- CCritSec m_CritSec; // CritSec to guard above attributes
-
- // Event to fire when we can come
- // out of blocking, or to come out of waiting
- // to discard if we change our minds.
- //
- CAMEvent m_StreamEvent;
-
- // All of these methods execute immediately. Helpers for others.
- //
- void ExecuteStop();
- void ExecuteStart();
- void CancelStop();
- void CancelStart();
-
- // Some things we need to be told by our owning filter
- // Your pin must also expose IAMStreamControl when QI'd for it!
- //
- IReferenceClock * m_pRefClock; // Need it to set advises
- // Filter must tell us via
- // SetSyncSource
- IMediaEventSink * m_pSink; // Event sink
- // Filter must tell us after it
- // creates it in JoinFilterGraph()
- FILTER_STATE m_FilterState; // Just need it!
- // Filter must tell us via
- // NotifyFilterState
- REFERENCE_TIME m_tRunStart; // Per the Run call to the filter
-
- // This guy will return one of the three StreamControlState's. Here's what
- // the caller should do for each one:
- //
- // STREAM_FLOWING: Proceed as usual (render or pass the sample on)
- // STREAM_DISCARDING: Calculate the time 'til *pSampleStop and wait
- // that long for the event handle
- // (GetStreamEventHandle()). If the wait
- // expires, throw the sample away. If the event
- // fires, call me back - I've changed my mind.
- //
- enum StreamControlState CheckSampleTimes( const REFERENCE_TIME * pSampleStart,
- const REFERENCE_TIME * pSampleStop );
-
-public:
- // You don't have to tell us much when we're created, but there are other
- // obligations that must be met. See SetSyncSource & NotifyFilterState
- // below.
- //
- CBaseStreamControl();
- ~CBaseStreamControl();
-
- // If you want this class to work properly, there are thing you need to
- // (keep) telling it. Filters with pins that use this class
- // should ensure that they pass through to this method any calls they
- // receive on their SetSyncSource.
-
- // We need a clock to see what time it is. This is for the
- // "discard in a timely fashion" logic. If we discard everything as
- // quick as possible, a whole 60 minute file could get discarded in the
- // first 10 seconds, and if somebody wants to turn streaming on at 30
- // minutes into the file, and they make the call more than a few seconds
- // after the graph is run, it may be too late!
- // So we hold every sample until it's time has gone, then we discard it.
- // The filter should call this when it gets a SetSyncSource
- //
- void SetSyncSource( IReferenceClock * pRefClock )
- {
- CAutoLock lck(&m_CritSec);
- if (m_pRefClock) m_pRefClock->Release();
- m_pRefClock = pRefClock;
- if (m_pRefClock) m_pRefClock->AddRef();
- }
-
- // Set event sink for notifications
- // The filter should call this in its JoinFilterGraph after it creates the
- // IMediaEventSink
- //
- void SetFilterGraph( IMediaEventSink *pSink ) {
- m_pSink = pSink;
- }
-
- // Since we schedule in stream time, we need the tStart and must track the
- // state of our owning filter.
- // The app should call this ever state change
- //
- void NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart = 0 );
-
- // Filter should call Flushing(TRUE) in BeginFlush,
- // and Flushing(FALSE) in EndFlush.
- //
- void Flushing( BOOL bInProgress );
-
-
- // The two main methods of IAMStreamControl
-
- // Class adds default values suitable for immediate
- // muting and unmuting of the stream.
-
- STDMETHODIMP StopAt( const REFERENCE_TIME * ptStop = NULL,
- BOOL bSendExtra = FALSE,
- DWORD dwCookie = 0 );
- STDMETHODIMP StartAt( const REFERENCE_TIME * ptStart = NULL,
- DWORD dwCookie = 0 );
- STDMETHODIMP GetInfo( AM_STREAM_INFO *pInfo);
-
- // Helper function for pin's receive method. Call this with
- // the sample and we'll tell you what to do with it. We'll do a
- // WaitForSingleObject within this call if one is required. This is
- // a "What should I do with this sample?" kind of call. We'll tell the
- // caller to either flow it or discard it.
- // If pSample is NULL we evaluate based on the current state
- // settings
- enum StreamControlState CheckStreamState( IMediaSample * pSample );
-
-private:
- // These don't require locking, but we are relying on the fact that
- // m_StreamState can be retrieved with integrity, and is a snap shot that
- // may have just been, or may be just about to be, changed.
- HANDLE GetStreamEventHandle() const { return m_StreamEvent; }
- enum StreamControlState GetStreamState() const { return m_StreamState; }
- BOOL IsStreaming() const { return m_StreamState == STREAM_FLOWING; }
-};
-
-#endif
+//------------------------------------------------------------------------------
+// File: StrmCtl.h
+//
+// Desc: DirectShow base classes.
+//
+// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __strmctl_h__
+#define __strmctl_h__
+
+class CBaseStreamControl : public IAMStreamControl
+{
+public:
+ // Used by the implementation
+ enum StreamControlState
+ { STREAM_FLOWING = 0x1000,
+ STREAM_DISCARDING
+ };
+
+private:
+ enum StreamControlState m_StreamState; // Current stream state
+ enum StreamControlState m_StreamStateOnStop; // State after next stop
+ // (i.e.Blocking or Discarding)
+
+ REFERENCE_TIME m_tStartTime; // MAX_TIME implies none
+ REFERENCE_TIME m_tStopTime; // MAX_TIME implies none
+ DWORD m_dwStartCookie; // Cookie for notification to app
+ DWORD m_dwStopCookie; // Cookie for notification to app
+ volatile BOOL m_bIsFlushing; // No optimization pls!
+ volatile BOOL m_bStopSendExtra; // bSendExtra was set
+ volatile BOOL m_bStopExtraSent; // the extra one was sent
+
+ CCritSec m_CritSec; // CritSec to guard above attributes
+
+ // Event to fire when we can come
+ // out of blocking, or to come out of waiting
+ // to discard if we change our minds.
+ //
+ CAMEvent m_StreamEvent;
+
+ // All of these methods execute immediately. Helpers for others.
+ //
+ void ExecuteStop();
+ void ExecuteStart();
+ void CancelStop();
+ void CancelStart();
+
+ // Some things we need to be told by our owning filter
+ // Your pin must also expose IAMStreamControl when QI'd for it!
+ //
+ IReferenceClock * m_pRefClock; // Need it to set advises
+ // Filter must tell us via
+ // SetSyncSource
+ IMediaEventSink * m_pSink; // Event sink
+ // Filter must tell us after it
+ // creates it in JoinFilterGraph()
+ FILTER_STATE m_FilterState; // Just need it!
+ // Filter must tell us via
+ // NotifyFilterState
+ REFERENCE_TIME m_tRunStart; // Per the Run call to the filter
+
+ // This guy will return one of the three StreamControlState's. Here's what
+ // the caller should do for each one:
+ //
+ // STREAM_FLOWING: Proceed as usual (render or pass the sample on)
+ // STREAM_DISCARDING: Calculate the time 'til *pSampleStop and wait
+ // that long for the event handle
+ // (GetStreamEventHandle()). If the wait
+ // expires, throw the sample away. If the event
+ // fires, call me back - I've changed my mind.
+ //
+ enum StreamControlState CheckSampleTimes( __in const REFERENCE_TIME * pSampleStart,
+ __in const REFERENCE_TIME * pSampleStop );
+
+public:
+ // You don't have to tell us much when we're created, but there are other
+ // obligations that must be met. See SetSyncSource & NotifyFilterState
+ // below.
+ //
+ CBaseStreamControl(__inout_opt HRESULT *phr = NULL);
+ ~CBaseStreamControl();
+
+ // If you want this class to work properly, there are thing you need to
+ // (keep) telling it. Filters with pins that use this class
+ // should ensure that they pass through to this method any calls they
+ // receive on their SetSyncSource.
+
+ // We need a clock to see what time it is. This is for the
+ // "discard in a timely fashion" logic. If we discard everything as
+ // quick as possible, a whole 60 minute file could get discarded in the
+ // first 10 seconds, and if somebody wants to turn streaming on at 30
+ // minutes into the file, and they make the call more than a few seconds
+ // after the graph is run, it may be too late!
+ // So we hold every sample until it's time has gone, then we discard it.
+ // The filter should call this when it gets a SetSyncSource
+ //
+ void SetSyncSource( IReferenceClock * pRefClock )
+ {
+ CAutoLock lck(&m_CritSec);
+ if (m_pRefClock) m_pRefClock->Release();
+ m_pRefClock = pRefClock;
+ if (m_pRefClock) m_pRefClock->AddRef();
+ }
+
+ // Set event sink for notifications
+ // The filter should call this in its JoinFilterGraph after it creates the
+ // IMediaEventSink
+ //
+ void SetFilterGraph( IMediaEventSink *pSink ) {
+ m_pSink = pSink;
+ }
+
+ // Since we schedule in stream time, we need the tStart and must track the
+ // state of our owning filter.
+ // The app should call this ever state change
+ //
+ void NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart = 0 );
+
+ // Filter should call Flushing(TRUE) in BeginFlush,
+ // and Flushing(FALSE) in EndFlush.
+ //
+ void Flushing( BOOL bInProgress );
+
+
+ // The two main methods of IAMStreamControl
+
+ // Class adds default values suitable for immediate
+ // muting and unmuting of the stream.
+
+ STDMETHODIMP StopAt( const REFERENCE_TIME * ptStop = NULL,
+ BOOL bSendExtra = FALSE,
+ DWORD dwCookie = 0 );
+ STDMETHODIMP StartAt( const REFERENCE_TIME * ptStart = NULL,
+ DWORD dwCookie = 0 );
+ STDMETHODIMP GetInfo( __out AM_STREAM_INFO *pInfo);
+
+ // Helper function for pin's receive method. Call this with
+ // the sample and we'll tell you what to do with it. We'll do a
+ // WaitForSingleObject within this call if one is required. This is
+ // a "What should I do with this sample?" kind of call. We'll tell the
+ // caller to either flow it or discard it.
+ // If pSample is NULL we evaluate based on the current state
+ // settings
+ enum StreamControlState CheckStreamState( IMediaSample * pSample );
+
+private:
+ // These don't require locking, but we are relying on the fact that
+ // m_StreamState can be retrieved with integrity, and is a snap shot that
+ // may have just been, or may be just about to be, changed.
+ HANDLE GetStreamEventHandle() const { return m_StreamEvent; }
+ enum StreamControlState GetStreamState() const { return m_StreamState; }
+ BOOL IsStreaming() const { return m_StreamState == STREAM_FLOWING; }
+};
+
+#endif
diff --git a/dshow_base/sysclock.cpp b/dshow_base/sysclock.cpp
new file mode 100644
index 0000000..0d58291
--- /dev/null
+++ b/dshow_base/sysclock.cpp
@@ -0,0 +1,74 @@
+//------------------------------------------------------------------------------
+// File: SysClock.cpp
+//
+// Desc: DirectShow base classes - implements a system clock based on
+// IReferenceClock.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#include
+
+
+#ifdef FILTER_DLL
+
+/* List of class IDs and creator functions for the class factory. This
+ provides the link between the OLE entry point in the DLL and an object
+ being created. The class factory will call the static CreateInstance
+ function when it is asked to create a CLSID_SystemClock object */
+
+CFactoryTemplate g_Templates[1] = {
+ {&CLSID_SystemClock, CSystemClock::CreateInstance}
+};
+
+int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
+#endif
+
+/* This goes in the factory template table to create new instances */
+CUnknown * WINAPI CSystemClock::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)
+{
+ return new CSystemClock(NAME("System reference clock"),pUnk, phr);
+}
+
+
+CSystemClock::CSystemClock(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr) :
+ CBaseReferenceClock(pName, pUnk, phr)
+{
+}
+
+STDMETHODIMP CSystemClock::NonDelegatingQueryInterface(
+ REFIID riid,
+ __deref_out void ** ppv)
+{
+ if (riid == IID_IPersist)
+ {
+ return GetInterface(static_cast(this), ppv);
+ }
+ else if (riid == IID_IAMClockAdjust)
+ {
+ return GetInterface(static_cast(this), ppv);
+ }
+ else
+ {
+ return CBaseReferenceClock::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+/* Return the clock's clsid */
+STDMETHODIMP
+CSystemClock::GetClassID(__out CLSID *pClsID)
+{
+ CheckPointer(pClsID,E_POINTER);
+ ValidateReadWritePtr(pClsID,sizeof(CLSID));
+ *pClsID = CLSID_SystemClock;
+ return NOERROR;
+}
+
+
+STDMETHODIMP
+CSystemClock::SetClockDelta(REFERENCE_TIME rtDelta)
+{
+ return SetTimeDelta(rtDelta);
+}
diff --git a/dshow_base/sysclock.h b/dshow_base/sysclock.h
index d55015f..3976d34 100644
--- a/dshow_base/sysclock.h
+++ b/dshow_base/sysclock.h
@@ -1,39 +1,39 @@
-//------------------------------------------------------------------------------
-// File: SysClock.h
-//
-// Desc: DirectShow base classes - defines a system clock implementation of
-// IReferenceClock.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-#ifndef __SYSTEMCLOCK__
-#define __SYSTEMCLOCK__
-
-//
-// Base clock. Uses timeGetTime ONLY
-// Uses most of the code in the base reference clock.
-// Provides GetTime
-//
-
-class CSystemClock : public CBaseReferenceClock, public IAMClockAdjust, public IPersist
-{
-public:
- // We must be able to create an instance of ourselves
- static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
- CSystemClock(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr);
-
- DECLARE_IUNKNOWN
-
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void ** ppv);
-
- // Yield up our class id so that we can be persisted
- // Implement required Ipersist method
- STDMETHODIMP GetClassID(CLSID *pClsID);
-
- // IAMClockAdjust methods
- STDMETHODIMP SetClockDelta(REFERENCE_TIME rtDelta);
-}; //CSystemClock
-
-#endif /* __SYSTEMCLOCK__ */
+//------------------------------------------------------------------------------
+// File: SysClock.h
+//
+// Desc: DirectShow base classes - defines a system clock implementation of
+// IReferenceClock.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#ifndef __SYSTEMCLOCK__
+#define __SYSTEMCLOCK__
+
+//
+// Base clock. Uses timeGetTime ONLY
+// Uses most of the code in the base reference clock.
+// Provides GetTime
+//
+
+class CSystemClock : public CBaseReferenceClock, public IAMClockAdjust, public IPersist
+{
+public:
+ // We must be able to create an instance of ourselves
+ static CUnknown * WINAPI CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);
+ CSystemClock(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);
+
+ DECLARE_IUNKNOWN
+
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
+
+ // Yield up our class id so that we can be persisted
+ // Implement required Ipersist method
+ STDMETHODIMP GetClassID(__out CLSID *pClsID);
+
+ // IAMClockAdjust methods
+ STDMETHODIMP SetClockDelta(REFERENCE_TIME rtDelta);
+}; //CSystemClock
+
+#endif /* __SYSTEMCLOCK__ */
diff --git a/dshow_base/transfrm.cpp b/dshow_base/transfrm.cpp
new file mode 100644
index 0000000..3d17077
--- /dev/null
+++ b/dshow_base/transfrm.cpp
@@ -0,0 +1,1016 @@
+//------------------------------------------------------------------------------
+// File: Transfrm.cpp
+//
+// Desc: DirectShow base classes - implements class for simple transform
+// filters such as video decompressors.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include
+#include
+
+
+// =================================================================
+// Implements the CTransformFilter class
+// =================================================================
+
+CTransformFilter::CTransformFilter(__in_opt LPCTSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ REFCLSID clsid) :
+ CBaseFilter(pName,pUnk,&m_csFilter, clsid),
+ m_pInput(NULL),
+ m_pOutput(NULL),
+ m_bEOSDelivered(FALSE),
+ m_bQualityChanged(FALSE),
+ m_bSampleSkipped(FALSE)
+{
+#ifdef PERF
+ RegisterPerfId();
+#endif // PERF
+}
+
+#ifdef UNICODE
+CTransformFilter::CTransformFilter(__in_opt LPCSTR pName,
+ __inout_opt LPUNKNOWN pUnk,
+ REFCLSID clsid) :
+ CBaseFilter(pName,pUnk,&m_csFilter, clsid),
+ m_pInput(NULL),
+ m_pOutput(NULL),
+ m_bEOSDelivered(FALSE),
+ m_bQualityChanged(FALSE),
+ m_bSampleSkipped(FALSE)
+{
+#ifdef PERF
+ RegisterPerfId();
+#endif // PERF
+}
+#endif
+
+// destructor
+
+CTransformFilter::~CTransformFilter()
+{
+ // Delete the pins
+
+ delete m_pInput;
+ delete m_pOutput;
+}
+
+
+// Transform place holder - should never be called
+HRESULT CTransformFilter::Transform(IMediaSample * pIn, IMediaSample *pOut)
+{
+ UNREFERENCED_PARAMETER(pIn);
+ UNREFERENCED_PARAMETER(pOut);
+ DbgBreak("CTransformFilter::Transform() should never be called");
+ return E_UNEXPECTED;
+}
+
+
+// return the number of pins we provide
+
+int CTransformFilter::GetPinCount()
+{
+ return 2;
+}
+
+
+// return a non-addrefed CBasePin * for the user to addref if he holds onto it
+// for longer than his pointer to us. We create the pins dynamically when they
+// are asked for rather than in the constructor. This is because we want to
+// give the derived class an oppportunity to return different pin objects
+
+// We return the objects as and when they are needed. If either of these fails
+// then we return NULL, the assumption being that the caller will realise the
+// whole deal is off and destroy us - which in turn will delete everything.
+
+CBasePin *
+CTransformFilter::GetPin(int n)
+{
+ HRESULT hr = S_OK;
+
+ // Create an input pin if necessary
+
+ if (m_pInput == NULL) {
+
+ m_pInput = new CTransformInputPin(NAME("Transform input pin"),
+ this, // Owner filter
+ &hr, // Result code
+ L"XForm In"); // Pin name
+
+
+ // Can't fail
+ ASSERT(SUCCEEDED(hr));
+ if (m_pInput == NULL) {
+ return NULL;
+ }
+ m_pOutput = (CTransformOutputPin *)
+ new CTransformOutputPin(NAME("Transform output pin"),
+ this, // Owner filter
+ &hr, // Result code
+ L"XForm Out"); // Pin name
+
+
+ // Can't fail
+ ASSERT(SUCCEEDED(hr));
+ if (m_pOutput == NULL) {
+ delete m_pInput;
+ m_pInput = NULL;
+ }
+ }
+
+ // Return the appropriate pin
+
+ if (n == 0) {
+ return m_pInput;
+ } else
+ if (n == 1) {
+ return m_pOutput;
+ } else {
+ return NULL;
+ }
+}
+
+
+//
+// FindPin
+//
+// If Id is In or Out then return the IPin* for that pin
+// creating the pin if need be. Otherwise return NULL with an error.
+
+STDMETHODIMP CTransformFilter::FindPin(LPCWSTR Id, __deref_out IPin **ppPin)
+{
+ CheckPointer(ppPin,E_POINTER);
+ ValidateReadWritePtr(ppPin,sizeof(IPin *));
+
+ if (0==lstrcmpW(Id,L"In")) {
+ *ppPin = GetPin(0);
+ } else if (0==lstrcmpW(Id,L"Out")) {
+ *ppPin = GetPin(1);
+ } else {
+ *ppPin = NULL;
+ return VFW_E_NOT_FOUND;
+ }
+
+ HRESULT hr = NOERROR;
+ // AddRef() returned pointer - but GetPin could fail if memory is low.
+ if (*ppPin) {
+ (*ppPin)->AddRef();
+ } else {
+ hr = E_OUTOFMEMORY; // probably. There's no pin anyway.
+ }
+ return hr;
+}
+
+
+// override these two functions if you want to inform something
+// about entry to or exit from streaming state.
+
+HRESULT
+CTransformFilter::StartStreaming()
+{
+ return NOERROR;
+}
+
+
+HRESULT
+CTransformFilter::StopStreaming()
+{
+ return NOERROR;
+}
+
+
+// override this to grab extra interfaces on connection
+
+HRESULT
+CTransformFilter::CheckConnect(PIN_DIRECTION dir, IPin *pPin)
+{
+ UNREFERENCED_PARAMETER(dir);
+ UNREFERENCED_PARAMETER(pPin);
+ return NOERROR;
+}
+
+
+// place holder to allow derived classes to release any extra interfaces
+
+HRESULT
+CTransformFilter::BreakConnect(PIN_DIRECTION dir)
+{
+ UNREFERENCED_PARAMETER(dir);
+ return NOERROR;
+}
+
+
+// Let derived classes know about connection completion
+
+HRESULT
+CTransformFilter::CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin)
+{
+ UNREFERENCED_PARAMETER(direction);
+ UNREFERENCED_PARAMETER(pReceivePin);
+ return NOERROR;
+}
+
+
+// override this to know when the media type is really set
+
+HRESULT
+CTransformFilter::SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt)
+{
+ UNREFERENCED_PARAMETER(direction);
+ UNREFERENCED_PARAMETER(pmt);
+ return NOERROR;
+}
+
+
+// Set up our output sample
+HRESULT
+CTransformFilter::InitializeOutputSample(IMediaSample *pSample, __deref_out IMediaSample **ppOutSample)
+{
+ IMediaSample *pOutSample;
+
+ // default - times are the same
+
+ AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
+ DWORD dwFlags = m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0;
+
+ // This will prevent the image renderer from switching us to DirectDraw
+ // when we can't do it without skipping frames because we're not on a
+ // keyframe. If it really has to switch us, it still will, but then we
+ // will have to wait for the next keyframe
+ if (!(pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT)) {
+ dwFlags |= AM_GBF_NOTASYNCPOINT;
+ }
+
+ ASSERT(m_pOutput->m_pAllocator != NULL);
+ HRESULT hr = m_pOutput->m_pAllocator->GetBuffer(
+ &pOutSample
+ , pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID ?
+ &pProps->tStart : NULL
+ , pProps->dwSampleFlags & AM_SAMPLE_STOPVALID ?
+ &pProps->tStop : NULL
+ , dwFlags
+ );
+ *ppOutSample = pOutSample;
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ ASSERT(pOutSample);
+ IMediaSample2 *pOutSample2;
+ if (SUCCEEDED(pOutSample->QueryInterface(IID_IMediaSample2,
+ (void **)&pOutSample2))) {
+ /* Modify it */
+ AM_SAMPLE2_PROPERTIES OutProps;
+ EXECUTE_ASSERT(SUCCEEDED(pOutSample2->GetProperties(
+ FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, tStart), (PBYTE)&OutProps)
+ ));
+ OutProps.dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
+ OutProps.dwSampleFlags =
+ (OutProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED) |
+ (pProps->dwSampleFlags & ~AM_SAMPLE_TYPECHANGED);
+ OutProps.tStart = pProps->tStart;
+ OutProps.tStop = pProps->tStop;
+ OutProps.cbData = FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId);
+ hr = pOutSample2->SetProperties(
+ FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId),
+ (PBYTE)&OutProps
+ );
+ if (pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {
+ m_bSampleSkipped = FALSE;
+ }
+ pOutSample2->Release();
+ } else {
+ if (pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) {
+ pOutSample->SetTime(&pProps->tStart,
+ &pProps->tStop);
+ }
+ if (pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT) {
+ pOutSample->SetSyncPoint(TRUE);
+ }
+ if (pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {
+ pOutSample->SetDiscontinuity(TRUE);
+ m_bSampleSkipped = FALSE;
+ }
+ // Copy the media times
+
+ LONGLONG MediaStart, MediaEnd;
+ if (pSample->GetMediaTime(&MediaStart,&MediaEnd) == NOERROR) {
+ pOutSample->SetMediaTime(&MediaStart,&MediaEnd);
+ }
+ }
+ return S_OK;
+}
+
+// override this to customize the transform process
+
+HRESULT
+CTransformFilter::Receive(IMediaSample *pSample)
+{
+ /* Check for other streams and pass them on */
+ AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
+ if (pProps->dwStreamId != AM_STREAM_MEDIA) {
+ return m_pOutput->m_pInputPin->Receive(pSample);
+ }
+ HRESULT hr;
+ ASSERT(pSample);
+ IMediaSample * pOutSample;
+
+ // If no output to deliver to then no point sending us data
+
+ ASSERT (m_pOutput != NULL) ;
+
+ // Set up the output sample
+ hr = InitializeOutputSample(pSample, &pOutSample);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // Start timing the transform (if PERF is defined)
+ MSR_START(m_idTransform);
+
+ // have the derived class transform the data
+
+ hr = Transform(pSample, pOutSample);
+
+ // Stop the clock and log it (if PERF is defined)
+ MSR_STOP(m_idTransform);
+
+ if (FAILED(hr)) {
+ DbgLog((LOG_TRACE,1,TEXT("Error from transform")));
+ } else {
+ // the Transform() function can return S_FALSE to indicate that the
+ // sample should not be delivered; we only deliver the sample if it's
+ // really S_OK (same as NOERROR, of course.)
+ if (hr == NOERROR) {
+ hr = m_pOutput->m_pInputPin->Receive(pOutSample);
+ m_bSampleSkipped = FALSE; // last thing no longer dropped
+ } else {
+ // S_FALSE returned from Transform is a PRIVATE agreement
+ // We should return NOERROR from Receive() in this cause because returning S_FALSE
+ // from Receive() means that this is the end of the stream and no more data should
+ // be sent.
+ if (S_FALSE == hr) {
+
+ // Release the sample before calling notify to avoid
+ // deadlocks if the sample holds a lock on the system
+ // such as DirectDraw buffers do
+ pOutSample->Release();
+ m_bSampleSkipped = TRUE;
+ if (!m_bQualityChanged) {
+ NotifyEvent(EC_QUALITY_CHANGE,0,0);
+ m_bQualityChanged = TRUE;
+ }
+ return NOERROR;
+ }
+ }
+ }
+
+ // release the output buffer. If the connected pin still needs it,
+ // it will have addrefed it itself.
+ pOutSample->Release();
+
+ return hr;
+}
+
+
+// Return S_FALSE to mean "pass the note on upstream"
+// Return NOERROR (Same as S_OK)
+// to mean "I've done something about it, don't pass it on"
+HRESULT CTransformFilter::AlterQuality(Quality q)
+{
+ UNREFERENCED_PARAMETER(q);
+ return S_FALSE;
+}
+
+
+// EndOfStream received. Default behaviour is to deliver straight
+// downstream, since we have no queued data. If you overrode Receive
+// and have queue data, then you need to handle this and deliver EOS after
+// all queued data is sent
+HRESULT
+CTransformFilter::EndOfStream(void)
+{
+ HRESULT hr = NOERROR;
+ if (m_pOutput != NULL) {
+ hr = m_pOutput->DeliverEndOfStream();
+ }
+
+ return hr;
+}
+
+
+// enter flush state. Receives already blocked
+// must override this if you have queued data or a worker thread
+HRESULT
+CTransformFilter::BeginFlush(void)
+{
+ HRESULT hr = NOERROR;
+ if (m_pOutput != NULL) {
+ // block receives -- done by caller (CBaseInputPin::BeginFlush)
+
+ // discard queued data -- we have no queued data
+
+ // free anyone blocked on receive - not possible in this filter
+
+ // call downstream
+ hr = m_pOutput->DeliverBeginFlush();
+ }
+ return hr;
+}
+
+
+// leave flush state. must override this if you have queued data
+// or a worker thread
+HRESULT
+CTransformFilter::EndFlush(void)
+{
+ // sync with pushing thread -- we have no worker thread
+
+ // ensure no more data to go downstream -- we have no queued data
+
+ // call EndFlush on downstream pins
+ ASSERT (m_pOutput != NULL);
+ return m_pOutput->DeliverEndFlush();
+
+ // caller (the input pin's method) will unblock Receives
+}
+
+
+// override these so that the derived filter can catch them
+
+STDMETHODIMP
+CTransformFilter::Stop()
+{
+ CAutoLock lck1(&m_csFilter);
+ if (m_State == State_Stopped) {
+ return NOERROR;
+ }
+
+ // Succeed the Stop if we are not completely connected
+
+ ASSERT(m_pInput == NULL || m_pOutput != NULL);
+ if (m_pInput == NULL || m_pInput->IsConnected() == FALSE ||
+ m_pOutput->IsConnected() == FALSE) {
+ m_State = State_Stopped;
+ m_bEOSDelivered = FALSE;
+ return NOERROR;
+ }
+
+ ASSERT(m_pInput);
+ ASSERT(m_pOutput);
+
+ // decommit the input pin before locking or we can deadlock
+ m_pInput->Inactive();
+
+ // synchronize with Receive calls
+
+ CAutoLock lck2(&m_csReceive);
+ m_pOutput->Inactive();
+
+ // allow a class derived from CTransformFilter
+ // to know about starting and stopping streaming
+
+ HRESULT hr = StopStreaming();
+ if (SUCCEEDED(hr)) {
+ // complete the state transition
+ m_State = State_Stopped;
+ m_bEOSDelivered = FALSE;
+ }
+ return hr;
+}
+
+
+STDMETHODIMP
+CTransformFilter::Pause()
+{
+ CAutoLock lck(&m_csFilter);
+ HRESULT hr = NOERROR;
+
+ if (m_State == State_Paused) {
+ // (This space left deliberately blank)
+ }
+
+ // If we have no input pin or it isn't yet connected then when we are
+ // asked to pause we deliver an end of stream to the downstream filter.
+ // This makes sure that it doesn't sit there forever waiting for
+ // samples which we cannot ever deliver without an input connection.
+
+ else if (m_pInput == NULL || m_pInput->IsConnected() == FALSE) {
+ if (m_pOutput && m_bEOSDelivered == FALSE) {
+ m_pOutput->DeliverEndOfStream();
+ m_bEOSDelivered = TRUE;
+ }
+ m_State = State_Paused;
+ }
+
+ // We may have an input connection but no output connection
+ // However, if we have an input pin we do have an output pin
+
+ else if (m_pOutput->IsConnected() == FALSE) {
+ m_State = State_Paused;
+ }
+
+ else {
+ if (m_State == State_Stopped) {
+ // allow a class derived from CTransformFilter
+ // to know about starting and stopping streaming
+ CAutoLock lck2(&m_csReceive);
+ hr = StartStreaming();
+ }
+ if (SUCCEEDED(hr)) {
+ hr = CBaseFilter::Pause();
+ }
+ }
+
+ m_bSampleSkipped = FALSE;
+ m_bQualityChanged = FALSE;
+ return hr;
+}
+
+HRESULT
+CTransformFilter::NewSegment(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop,
+ double dRate)
+{
+ if (m_pOutput != NULL) {
+ return m_pOutput->DeliverNewSegment(tStart, tStop, dRate);
+ }
+ return S_OK;
+}
+
+// Check streaming status
+HRESULT
+CTransformInputPin::CheckStreaming()
+{
+ ASSERT(m_pTransformFilter->m_pOutput != NULL);
+ if (!m_pTransformFilter->m_pOutput->IsConnected()) {
+ return VFW_E_NOT_CONNECTED;
+ } else {
+ // Shouldn't be able to get any data if we're not connected!
+ ASSERT(IsConnected());
+
+ // we're flushing
+ if (m_bFlushing) {
+ return S_FALSE;
+ }
+ // Don't process stuff in Stopped state
+ if (IsStopped()) {
+ return VFW_E_WRONG_STATE;
+ }
+ if (m_bRunTimeError) {
+ return VFW_E_RUNTIME_ERROR;
+ }
+ return S_OK;
+ }
+}
+
+
+// =================================================================
+// Implements the CTransformInputPin class
+// =================================================================
+
+
+// constructor
+
+CTransformInputPin::CTransformInputPin(
+ __in_opt LPCTSTR pObjectName,
+ __inout CTransformFilter *pTransformFilter,
+ __inout HRESULT * phr,
+ __in_opt LPCWSTR pName)
+ : CBaseInputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pName)
+{
+ DbgLog((LOG_TRACE,2,TEXT("CTransformInputPin::CTransformInputPin")));
+ m_pTransformFilter = pTransformFilter;
+}
+
+#ifdef UNICODE
+CTransformInputPin::CTransformInputPin(
+ __in_opt LPCSTR pObjectName,
+ __inout CTransformFilter *pTransformFilter,
+ __inout HRESULT * phr,
+ __in_opt LPCWSTR pName)
+ : CBaseInputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pName)
+{
+ DbgLog((LOG_TRACE,2,TEXT("CTransformInputPin::CTransformInputPin")));
+ m_pTransformFilter = pTransformFilter;
+}
+#endif
+
+// provides derived filter a chance to grab extra interfaces
+
+HRESULT
+CTransformInputPin::CheckConnect(IPin *pPin)
+{
+ HRESULT hr = m_pTransformFilter->CheckConnect(PINDIR_INPUT,pPin);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return CBaseInputPin::CheckConnect(pPin);
+}
+
+
+// provides derived filter a chance to release it's extra interfaces
+
+HRESULT
+CTransformInputPin::BreakConnect()
+{
+ // Can't disconnect unless stopped
+ ASSERT(IsStopped());
+ m_pTransformFilter->BreakConnect(PINDIR_INPUT);
+ return CBaseInputPin::BreakConnect();
+}
+
+
+// Let derived class know when the input pin is connected
+
+HRESULT
+CTransformInputPin::CompleteConnect(IPin *pReceivePin)
+{
+ HRESULT hr = m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return CBaseInputPin::CompleteConnect(pReceivePin);
+}
+
+
+// check that we can support a given media type
+
+HRESULT
+CTransformInputPin::CheckMediaType(const CMediaType* pmt)
+{
+ // Check the input type
+
+ HRESULT hr = m_pTransformFilter->CheckInputType(pmt);
+ if (S_OK != hr) {
+ return hr;
+ }
+
+ // if the output pin is still connected, then we have
+ // to check the transform not just the input format
+
+ if ((m_pTransformFilter->m_pOutput != NULL) &&
+ (m_pTransformFilter->m_pOutput->IsConnected())) {
+ return m_pTransformFilter->CheckTransform(
+ pmt,
+ &m_pTransformFilter->m_pOutput->CurrentMediaType());
+ } else {
+ return hr;
+ }
+}
+
+
+// set the media type for this connection
+
+HRESULT
+CTransformInputPin::SetMediaType(const CMediaType* mtIn)
+{
+ // Set the base class media type (should always succeed)
+ HRESULT hr = CBasePin::SetMediaType(mtIn);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // check the transform can be done (should always succeed)
+ ASSERT(SUCCEEDED(m_pTransformFilter->CheckInputType(mtIn)));
+
+ return m_pTransformFilter->SetMediaType(PINDIR_INPUT,mtIn);
+}
+
+
+// =================================================================
+// Implements IMemInputPin interface
+// =================================================================
+
+
+// provide EndOfStream that passes straight downstream
+// (there is no queued data)
+STDMETHODIMP
+CTransformInputPin::EndOfStream(void)
+{
+ CAutoLock lck(&m_pTransformFilter->m_csReceive);
+ HRESULT hr = CheckStreaming();
+ if (S_OK == hr) {
+ hr = m_pTransformFilter->EndOfStream();
+ }
+ return hr;
+}
+
+
+// enter flushing state. Call default handler to block Receives, then
+// pass to overridable method in filter
+STDMETHODIMP
+CTransformInputPin::BeginFlush(void)
+{
+ CAutoLock lck(&m_pTransformFilter->m_csFilter);
+ // Are we actually doing anything?
+ ASSERT(m_pTransformFilter->m_pOutput != NULL);
+ if (!IsConnected() ||
+ !m_pTransformFilter->m_pOutput->IsConnected()) {
+ return VFW_E_NOT_CONNECTED;
+ }
+ HRESULT hr = CBaseInputPin::BeginFlush();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return m_pTransformFilter->BeginFlush();
+}
+
+
+// leave flushing state.
+// Pass to overridable method in filter, then call base class
+// to unblock receives (finally)
+STDMETHODIMP
+CTransformInputPin::EndFlush(void)
+{
+ CAutoLock lck(&m_pTransformFilter->m_csFilter);
+ // Are we actually doing anything?
+ ASSERT(m_pTransformFilter->m_pOutput != NULL);
+ if (!IsConnected() ||
+ !m_pTransformFilter->m_pOutput->IsConnected()) {
+ return VFW_E_NOT_CONNECTED;
+ }
+
+ HRESULT hr = m_pTransformFilter->EndFlush();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return CBaseInputPin::EndFlush();
+}
+
+
+// here's the next block of data from the stream.
+// AddRef it yourself if you need to hold it beyond the end
+// of this call.
+
+HRESULT
+CTransformInputPin::Receive(IMediaSample * pSample)
+{
+ HRESULT hr;
+ CAutoLock lck(&m_pTransformFilter->m_csReceive);
+ ASSERT(pSample);
+
+ // check all is well with the base class
+ hr = CBaseInputPin::Receive(pSample);
+ if (S_OK == hr) {
+ hr = m_pTransformFilter->Receive(pSample);
+ }
+ return hr;
+}
+
+
+
+
+// override to pass downstream
+STDMETHODIMP
+CTransformInputPin::NewSegment(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop,
+ double dRate)
+{
+ // Save the values in the pin
+ CBasePin::NewSegment(tStart, tStop, dRate);
+ return m_pTransformFilter->NewSegment(tStart, tStop, dRate);
+}
+
+
+
+
+// =================================================================
+// Implements the CTransformOutputPin class
+// =================================================================
+
+
+// constructor
+
+CTransformOutputPin::CTransformOutputPin(
+ __in_opt LPCTSTR pObjectName,
+ __inout CTransformFilter *pTransformFilter,
+ __inout HRESULT * phr,
+ __in_opt LPCWSTR pPinName)
+ : CBaseOutputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pPinName),
+ m_pPosition(NULL)
+{
+ DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::CTransformOutputPin")));
+ m_pTransformFilter = pTransformFilter;
+
+}
+
+#ifdef UNICODE
+CTransformOutputPin::CTransformOutputPin(
+ __in_opt LPCSTR pObjectName,
+ __inout CTransformFilter *pTransformFilter,
+ __inout HRESULT * phr,
+ __in_opt LPCWSTR pPinName)
+ : CBaseOutputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pPinName),
+ m_pPosition(NULL)
+{
+ DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::CTransformOutputPin")));
+ m_pTransformFilter = pTransformFilter;
+
+}
+#endif
+
+// destructor
+
+CTransformOutputPin::~CTransformOutputPin()
+{
+ DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::~CTransformOutputPin")));
+
+ if (m_pPosition) m_pPosition->Release();
+}
+
+
+// overriden to expose IMediaPosition and IMediaSeeking control interfaces
+
+STDMETHODIMP
+CTransformOutputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ CheckPointer(ppv,E_POINTER);
+ ValidateReadWritePtr(ppv,sizeof(PVOID));
+ *ppv = NULL;
+
+ if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {
+
+ // we should have an input pin by now
+
+ ASSERT(m_pTransformFilter->m_pInput != NULL);
+
+ if (m_pPosition == NULL) {
+
+ HRESULT hr = CreatePosPassThru(
+ GetOwner(),
+ FALSE,
+ (IPin *)m_pTransformFilter->m_pInput,
+ &m_pPosition);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+ return m_pPosition->QueryInterface(riid, ppv);
+ } else {
+ return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
+ }
+}
+
+
+// provides derived filter a chance to grab extra interfaces
+
+HRESULT
+CTransformOutputPin::CheckConnect(IPin *pPin)
+{
+ // we should have an input connection first
+
+ ASSERT(m_pTransformFilter->m_pInput != NULL);
+ if ((m_pTransformFilter->m_pInput->IsConnected() == FALSE)) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr = m_pTransformFilter->CheckConnect(PINDIR_OUTPUT,pPin);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return CBaseOutputPin::CheckConnect(pPin);
+}
+
+
+// provides derived filter a chance to release it's extra interfaces
+
+HRESULT
+CTransformOutputPin::BreakConnect()
+{
+ // Can't disconnect unless stopped
+ ASSERT(IsStopped());
+ m_pTransformFilter->BreakConnect(PINDIR_OUTPUT);
+ return CBaseOutputPin::BreakConnect();
+}
+
+
+// Let derived class know when the output pin is connected
+
+HRESULT
+CTransformOutputPin::CompleteConnect(IPin *pReceivePin)
+{
+ HRESULT hr = m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return CBaseOutputPin::CompleteConnect(pReceivePin);
+}
+
+
+// check a given transform - must have selected input type first
+
+HRESULT
+CTransformOutputPin::CheckMediaType(const CMediaType* pmtOut)
+{
+ // must have selected input first
+ ASSERT(m_pTransformFilter->m_pInput != NULL);
+ if ((m_pTransformFilter->m_pInput->IsConnected() == FALSE)) {
+ return E_INVALIDARG;
+ }
+
+ return m_pTransformFilter->CheckTransform(
+ &m_pTransformFilter->m_pInput->CurrentMediaType(),
+ pmtOut);
+}
+
+
+// called after we have agreed a media type to actually set it in which case
+// we run the CheckTransform function to get the output format type again
+
+HRESULT
+CTransformOutputPin::SetMediaType(const CMediaType* pmtOut)
+{
+ HRESULT hr = NOERROR;
+ ASSERT(m_pTransformFilter->m_pInput != NULL);
+
+ ASSERT(m_pTransformFilter->m_pInput->CurrentMediaType().IsValid());
+
+ // Set the base class media type (should always succeed)
+ hr = CBasePin::SetMediaType(pmtOut);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+#ifdef DEBUG
+ if (FAILED(m_pTransformFilter->CheckTransform(&m_pTransformFilter->
+ m_pInput->CurrentMediaType(),pmtOut))) {
+ DbgLog((LOG_ERROR,0,TEXT("*** This filter is accepting an output media type")));
+ DbgLog((LOG_ERROR,0,TEXT(" that it can't currently transform to. I hope")));
+ DbgLog((LOG_ERROR,0,TEXT(" it's smart enough to reconnect its input.")));
+ }
+#endif
+
+ return m_pTransformFilter->SetMediaType(PINDIR_OUTPUT,pmtOut);
+}
+
+
+// pass the buffer size decision through to the main transform class
+
+HRESULT
+CTransformOutputPin::DecideBufferSize(
+ IMemAllocator * pAllocator,
+ __inout ALLOCATOR_PROPERTIES* pProp)
+{
+ return m_pTransformFilter->DecideBufferSize(pAllocator, pProp);
+}
+
+
+
+// return a specific media type indexed by iPosition
+
+HRESULT
+CTransformOutputPin::GetMediaType(
+ int iPosition,
+ __inout CMediaType *pMediaType)
+{
+ ASSERT(m_pTransformFilter->m_pInput != NULL);
+
+ // We don't have any media types if our input is not connected
+
+ if (m_pTransformFilter->m_pInput->IsConnected()) {
+ return m_pTransformFilter->GetMediaType(iPosition,pMediaType);
+ } else {
+ return VFW_S_NO_MORE_ITEMS;
+ }
+}
+
+
+// Override this if you can do something constructive to act on the
+// quality message. Consider passing it upstream as well
+
+// Pass the quality mesage on upstream.
+
+STDMETHODIMP
+CTransformOutputPin::Notify(IBaseFilter * pSender, Quality q)
+{
+ UNREFERENCED_PARAMETER(pSender);
+ ValidateReadPtr(pSender,sizeof(IBaseFilter));
+
+ // First see if we want to handle this ourselves
+ HRESULT hr = m_pTransformFilter->AlterQuality(q);
+ if (hr!=S_FALSE) {
+ return hr; // either S_OK or a failure
+ }
+
+ // S_FALSE means we pass the message on.
+ // Find the quality sink for our input pin and send it there
+
+ ASSERT(m_pTransformFilter->m_pInput != NULL);
+
+ return m_pTransformFilter->m_pInput->PassNotify(q);
+
+} // Notify
+
+
+// the following removes a very large number of level 4 warnings from the microsoft
+// compiler output, which are not useful at all in this case.
+#pragma warning(disable:4514)
diff --git a/dshow_base/transfrm.h b/dshow_base/transfrm.h
index d4873fa..9b27647 100644
--- a/dshow_base/transfrm.h
+++ b/dshow_base/transfrm.h
@@ -1,304 +1,304 @@
-//------------------------------------------------------------------------------
-// File: Transfrm.h
-//
-// Desc: DirectShow base classes - defines classes from which simple
-// transform codecs may be derived.
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------------------------
-
-
-// It assumes the codec has one input and one output stream, and has no
-// interest in memory management, interface negotiation or anything else.
-//
-// derive your class from this, and supply Transform and the media type/format
-// negotiation functions. Implement that class, compile and link and
-// you're done.
-
-
-#ifndef __TRANSFRM__
-#define __TRANSFRM__
-
-// ======================================================================
-// This is the com object that represents a simple transform filter. It
-// supports IBaseFilter, IMediaFilter and two pins through nested interfaces
-// ======================================================================
-
-class CTransformFilter;
-
-// ==================================================
-// Implements the input pin
-// ==================================================
-
-class CTransformInputPin : public CBaseInputPin
-{
- friend class CTransformFilter;
-
-protected:
- CTransformFilter *m_pTransformFilter;
-
-
-public:
-
- CTransformInputPin(
- TCHAR *pObjectName,
- CTransformFilter *pTransformFilter,
- HRESULT * phr,
- LPCWSTR pName);
-#ifdef UNICODE
- CTransformInputPin(
- char *pObjectName,
- CTransformFilter *pTransformFilter,
- HRESULT * phr,
- LPCWSTR pName);
-#endif
-
- STDMETHODIMP QueryId(LPWSTR * Id)
- {
- return AMGetWideString(L"In", Id);
- }
-
- // Grab and release extra interfaces if required
-
- HRESULT CheckConnect(IPin *pPin);
- HRESULT BreakConnect();
- HRESULT CompleteConnect(IPin *pReceivePin);
-
- // check that we can support this output type
- HRESULT CheckMediaType(const CMediaType* mtIn);
-
- // set the connection media type
- HRESULT SetMediaType(const CMediaType* mt);
-
- // --- IMemInputPin -----
-
- // here's the next block of data from the stream.
- // AddRef it yourself if you need to hold it beyond the end
- // of this call.
- STDMETHODIMP Receive(IMediaSample * pSample);
-
- // provide EndOfStream that passes straight downstream
- // (there is no queued data)
- STDMETHODIMP EndOfStream(void);
-
- // passes it to CTransformFilter::BeginFlush
- STDMETHODIMP BeginFlush(void);
-
- // passes it to CTransformFilter::EndFlush
- STDMETHODIMP EndFlush(void);
-
- STDMETHODIMP NewSegment(
- REFERENCE_TIME tStart,
- REFERENCE_TIME tStop,
- double dRate);
-
- // Check if it's OK to process samples
- virtual HRESULT CheckStreaming();
-
- // Media type
-public:
- CMediaType& CurrentMediaType() { return m_mt; };
-
-};
-
-// ==================================================
-// Implements the output pin
-// ==================================================
-
-class CTransformOutputPin : public CBaseOutputPin
-{
- friend class CTransformFilter;
-
-protected:
- CTransformFilter *m_pTransformFilter;
-
-public:
-
- // implement IMediaPosition by passing upstream
- IUnknown * m_pPosition;
-
- CTransformOutputPin(
- TCHAR *pObjectName,
- CTransformFilter *pTransformFilter,
- HRESULT * phr,
- LPCWSTR pName);
-#ifdef UNICODE
- CTransformOutputPin(
- CHAR *pObjectName,
- CTransformFilter *pTransformFilter,
- HRESULT * phr,
- LPCWSTR pName);
-#endif
- ~CTransformOutputPin();
-
- // override to expose IMediaPosition
- STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
-
- // --- CBaseOutputPin ------------
-
- STDMETHODIMP QueryId(LPWSTR * Id)
- {
- return AMGetWideString(L"Out", Id);
- }
-
- // Grab and release extra interfaces if required
-
- HRESULT CheckConnect(IPin *pPin);
- HRESULT BreakConnect();
- HRESULT CompleteConnect(IPin *pReceivePin);
-
- // check that we can support this output type
- HRESULT CheckMediaType(const CMediaType* mtOut);
-
- // set the connection media type
- HRESULT SetMediaType(const CMediaType *pmt);
-
- // called from CBaseOutputPin during connection to ask for
- // the count and size of buffers we need.
- HRESULT DecideBufferSize(
- IMemAllocator * pAlloc,
- ALLOCATOR_PROPERTIES *pProp);
-
- // returns the preferred formats for a pin
- HRESULT GetMediaType(int iPosition,CMediaType *pMediaType);
-
- // inherited from IQualityControl via CBasePin
- STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
-
- // Media type
-public:
- CMediaType& CurrentMediaType() { return m_mt; };
-};
-
-
-class AM_NOVTABLE CTransformFilter : public CBaseFilter
-{
-
-public:
-
- // map getpin/getpincount for base enum of pins to owner
- // override this to return more specialised pin objects
-
- virtual int GetPinCount();
- virtual CBasePin * GetPin(int n);
- STDMETHODIMP FindPin(LPCWSTR Id, IPin **ppPin);
-
- // override state changes to allow derived transform filter
- // to control streaming start/stop
- STDMETHODIMP Stop();
- STDMETHODIMP Pause();
-
-public:
-
- CTransformFilter(TCHAR *, LPUNKNOWN, REFCLSID clsid);
-#ifdef UNICODE
- CTransformFilter(CHAR *, LPUNKNOWN, REFCLSID clsid);
-#endif
- ~CTransformFilter();
-
- // =================================================================
- // ----- override these bits ---------------------------------------
- // =================================================================
-
- // These must be supplied in a derived class
-
- virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);
-
- // check if you can support mtIn
- virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;
-
- // check if you can support the transform from this input to this output
- virtual HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut) PURE;
-
- // this goes in the factory template table to create new instances
- // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
-
- // call the SetProperties function with appropriate arguments
- virtual HRESULT DecideBufferSize(
- IMemAllocator * pAllocator,
- ALLOCATOR_PROPERTIES *pprop) PURE;
-
- // override to suggest OUTPUT pin media types
- virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE;
-
-
-
- // =================================================================
- // ----- Optional Override Methods -----------------------
- // =================================================================
-
- // you can also override these if you want to know about streaming
- virtual HRESULT StartStreaming();
- virtual HRESULT StopStreaming();
-
- // override if you can do anything constructive with quality notifications
- virtual HRESULT AlterQuality(Quality q);
-
- // override this to know when the media type is actually set
- virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);
-
- // chance to grab extra interfaces on connection
- virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);
- virtual HRESULT BreakConnect(PIN_DIRECTION dir);
- virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);
-
- // chance to customize the transform process
- virtual HRESULT Receive(IMediaSample *pSample);
-
- // Standard setup for output sample
- HRESULT InitializeOutputSample(IMediaSample *pSample, IMediaSample **ppOutSample);
-
- // if you override Receive, you may need to override these three too
- virtual HRESULT EndOfStream(void);
- virtual HRESULT BeginFlush(void);
- virtual HRESULT EndFlush(void);
- virtual HRESULT NewSegment(
- REFERENCE_TIME tStart,
- REFERENCE_TIME tStop,
- double dRate);
-
-#ifdef PERF
- // Override to register performance measurement with a less generic string
- // You should do this to avoid confusion with other filters
- virtual void RegisterPerfId()
- {m_idTransform = MSR_REGISTER(TEXT("Transform"));}
-#endif // PERF
-
-
-// implementation details
-
-protected:
-
-#ifdef PERF
- int m_idTransform; // performance measuring id
-#endif
- BOOL m_bEOSDelivered; // have we sent EndOfStream
- BOOL m_bSampleSkipped; // Did we just skip a frame
- BOOL m_bQualityChanged; // Have we degraded?
-
- // critical section protecting filter state.
-
- CCritSec m_csFilter;
-
- // critical section stopping state changes (ie Stop) while we're
- // processing a sample.
- //
- // This critical section is held when processing
- // events that occur on the receive thread - Receive() and EndOfStream().
- //
- // If you want to hold both m_csReceive and m_csFilter then grab
- // m_csFilter FIRST - like CTransformFilter::Stop() does.
-
- CCritSec m_csReceive;
-
- // these hold our input and output pins
-
- friend class CTransformInputPin;
- friend class CTransformOutputPin;
- CTransformInputPin *m_pInput;
- CTransformOutputPin *m_pOutput;
-};
-
-#endif /* __TRANSFRM__ */
-
-
+//------------------------------------------------------------------------------
+// File: Transfrm.h
+//
+// Desc: DirectShow base classes - defines classes from which simple
+// transform codecs may be derived.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+// It assumes the codec has one input and one output stream, and has no
+// interest in memory management, interface negotiation or anything else.
+//
+// derive your class from this, and supply Transform and the media type/format
+// negotiation functions. Implement that class, compile and link and
+// you're done.
+
+
+#ifndef __TRANSFRM__
+#define __TRANSFRM__
+
+// ======================================================================
+// This is the com object that represents a simple transform filter. It
+// supports IBaseFilter, IMediaFilter and two pins through nested interfaces
+// ======================================================================
+
+class CTransformFilter;
+
+// ==================================================
+// Implements the input pin
+// ==================================================
+
+class CTransformInputPin : public CBaseInputPin
+{
+ friend class CTransformFilter;
+
+protected:
+ CTransformFilter *m_pTransformFilter;
+
+
+public:
+
+ CTransformInputPin(
+ __in_opt LPCTSTR pObjectName,
+ __inout CTransformFilter *pTransformFilter,
+ __inout HRESULT * phr,
+ __in_opt LPCWSTR pName);
+#ifdef UNICODE
+ CTransformInputPin(
+ __in_opt LPCSTR pObjectName,
+ __inout CTransformFilter *pTransformFilter,
+ __inout HRESULT * phr,
+ __in_opt LPCWSTR pName);
+#endif
+
+ STDMETHODIMP QueryId(__deref_out LPWSTR * Id)
+ {
+ return AMGetWideString(L"In", Id);
+ }
+
+ // Grab and release extra interfaces if required
+
+ HRESULT CheckConnect(IPin *pPin);
+ HRESULT BreakConnect();
+ HRESULT CompleteConnect(IPin *pReceivePin);
+
+ // check that we can support this output type
+ HRESULT CheckMediaType(const CMediaType* mtIn);
+
+ // set the connection media type
+ HRESULT SetMediaType(const CMediaType* mt);
+
+ // --- IMemInputPin -----
+
+ // here's the next block of data from the stream.
+ // AddRef it yourself if you need to hold it beyond the end
+ // of this call.
+ STDMETHODIMP Receive(IMediaSample * pSample);
+
+ // provide EndOfStream that passes straight downstream
+ // (there is no queued data)
+ STDMETHODIMP EndOfStream(void);
+
+ // passes it to CTransformFilter::BeginFlush
+ STDMETHODIMP BeginFlush(void);
+
+ // passes it to CTransformFilter::EndFlush
+ STDMETHODIMP EndFlush(void);
+
+ STDMETHODIMP NewSegment(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop,
+ double dRate);
+
+ // Check if it's OK to process samples
+ virtual HRESULT CheckStreaming();
+
+ // Media type
+public:
+ CMediaType& CurrentMediaType() { return m_mt; };
+
+};
+
+// ==================================================
+// Implements the output pin
+// ==================================================
+
+class CTransformOutputPin : public CBaseOutputPin
+{
+ friend class CTransformFilter;
+
+protected:
+ CTransformFilter *m_pTransformFilter;
+
+public:
+
+ // implement IMediaPosition by passing upstream
+ IUnknown * m_pPosition;
+
+ CTransformOutputPin(
+ __in_opt LPCTSTR pObjectName,
+ __inout CTransformFilter *pTransformFilter,
+ __inout HRESULT * phr,
+ __in_opt LPCWSTR pName);
+#ifdef UNICODE
+ CTransformOutputPin(
+ __in_opt LPCSTR pObjectName,
+ __inout CTransformFilter *pTransformFilter,
+ __inout HRESULT * phr,
+ __in_opt LPCWSTR pName);
+#endif
+ ~CTransformOutputPin();
+
+ // override to expose IMediaPosition
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+ // --- CBaseOutputPin ------------
+
+ STDMETHODIMP QueryId(__deref_out LPWSTR * Id)
+ {
+ return AMGetWideString(L"Out", Id);
+ }
+
+ // Grab and release extra interfaces if required
+
+ HRESULT CheckConnect(IPin *pPin);
+ HRESULT BreakConnect();
+ HRESULT CompleteConnect(IPin *pReceivePin);
+
+ // check that we can support this output type
+ HRESULT CheckMediaType(const CMediaType* mtOut);
+
+ // set the connection media type
+ HRESULT SetMediaType(const CMediaType *pmt);
+
+ // called from CBaseOutputPin during connection to ask for
+ // the count and size of buffers we need.
+ HRESULT DecideBufferSize(
+ IMemAllocator * pAlloc,
+ __inout ALLOCATOR_PROPERTIES *pProp);
+
+ // returns the preferred formats for a pin
+ HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType);
+
+ // inherited from IQualityControl via CBasePin
+ STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
+
+ // Media type
+public:
+ CMediaType& CurrentMediaType() { return m_mt; };
+};
+
+
+class AM_NOVTABLE CTransformFilter : public CBaseFilter
+{
+
+public:
+
+ // map getpin/getpincount for base enum of pins to owner
+ // override this to return more specialised pin objects
+
+ virtual int GetPinCount();
+ virtual CBasePin * GetPin(int n);
+ STDMETHODIMP FindPin(LPCWSTR Id, __deref_out IPin **ppPin);
+
+ // override state changes to allow derived transform filter
+ // to control streaming start/stop
+ STDMETHODIMP Stop();
+ STDMETHODIMP Pause();
+
+public:
+
+ CTransformFilter(__in_opt LPCTSTR , __inout_opt LPUNKNOWN, REFCLSID clsid);
+#ifdef UNICODE
+ CTransformFilter(__in_opt LPCSTR , __inout_opt LPUNKNOWN, REFCLSID clsid);
+#endif
+ ~CTransformFilter();
+
+ // =================================================================
+ // ----- override these bits ---------------------------------------
+ // =================================================================
+
+ // These must be supplied in a derived class
+
+ virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);
+
+ // check if you can support mtIn
+ virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;
+
+ // check if you can support the transform from this input to this output
+ virtual HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut) PURE;
+
+ // this goes in the factory template table to create new instances
+ // static CCOMObject * CreateInstance(__inout_opt LPUNKNOWN, HRESULT *);
+
+ // call the SetProperties function with appropriate arguments
+ virtual HRESULT DecideBufferSize(
+ IMemAllocator * pAllocator,
+ __inout ALLOCATOR_PROPERTIES *pprop) PURE;
+
+ // override to suggest OUTPUT pin media types
+ virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType) PURE;
+
+
+
+ // =================================================================
+ // ----- Optional Override Methods -----------------------
+ // =================================================================
+
+ // you can also override these if you want to know about streaming
+ virtual HRESULT StartStreaming();
+ virtual HRESULT StopStreaming();
+
+ // override if you can do anything constructive with quality notifications
+ virtual HRESULT AlterQuality(Quality q);
+
+ // override this to know when the media type is actually set
+ virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);
+
+ // chance to grab extra interfaces on connection
+ virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);
+ virtual HRESULT BreakConnect(PIN_DIRECTION dir);
+ virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);
+
+ // chance to customize the transform process
+ virtual HRESULT Receive(IMediaSample *pSample);
+
+ // Standard setup for output sample
+ HRESULT InitializeOutputSample(IMediaSample *pSample, __deref_out IMediaSample **ppOutSample);
+
+ // if you override Receive, you may need to override these three too
+ virtual HRESULT EndOfStream(void);
+ virtual HRESULT BeginFlush(void);
+ virtual HRESULT EndFlush(void);
+ virtual HRESULT NewSegment(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop,
+ double dRate);
+
+#ifdef PERF
+ // Override to register performance measurement with a less generic string
+ // You should do this to avoid confusion with other filters
+ virtual void RegisterPerfId()
+ {m_idTransform = MSR_REGISTER(TEXT("Transform"));}
+#endif // PERF
+
+
+// implementation details
+
+protected:
+
+#ifdef PERF
+ int m_idTransform; // performance measuring id
+#endif
+ BOOL m_bEOSDelivered; // have we sent EndOfStream
+ BOOL m_bSampleSkipped; // Did we just skip a frame
+ BOOL m_bQualityChanged; // Have we degraded?
+
+ // critical section protecting filter state.
+
+ CCritSec m_csFilter;
+
+ // critical section stopping state changes (ie Stop) while we're
+ // processing a sample.
+ //
+ // This critical section is held when processing
+ // events that occur on the receive thread - Receive() and EndOfStream().
+ //
+ // If you want to hold both m_csReceive and m_csFilter then grab
+ // m_csFilter FIRST - like CTransformFilter::Stop() does.
+
+ CCritSec m_csReceive;
+
+ // these hold our input and output pins
+
+ friend class CTransformInputPin;
+ friend class CTransformOutputPin;
+ CTransformInputPin *m_pInput;
+ CTransformOutputPin *m_pOutput;
+};
+
+#endif /* __TRANSFRM__ */
+
+
diff --git a/dshow_base/transip.cpp b/dshow_base/transip.cpp
new file mode 100644
index 0000000..e8e12eb
--- /dev/null
+++ b/dshow_base/transip.cpp
@@ -0,0 +1,974 @@
+//------------------------------------------------------------------------------
+// File: TransIP.cpp
+//
+// Desc: DirectShow base classes - implements class for simple Transform-
+// In-Place filters such as audio.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+// How allocators are decided.
+//
+// An in-place transform tries to do its work in someone else's buffers.
+// It tries to persuade the filters on either side to use the same allocator
+// (and for that matter the same media type). In desperation, if the downstream
+// filter refuses to supply an allocator and the upstream filter offers only
+// a read-only one then it will provide an allocator.
+// if the upstream filter insists on a read-only allocator then the transform
+// filter will (reluctantly) copy the data before transforming it.
+//
+// In order to pass an allocator through it needs to remember the one it got
+// from the first connection to pass it on to the second one.
+//
+// It is good if we can avoid insisting on a particular order of connection
+// (There is a precedent for insisting on the input
+// being connected first. Insisting on the output being connected first is
+// not allowed. That would break RenderFile.)
+//
+// The base pin classes (CBaseOutputPin and CBaseInputPin) both have a
+// m_pAllocator member which is used in places like
+// CBaseOutputPin::GetDeliveryBuffer and CBaseInputPin::Inactive.
+// To avoid lots of extra overriding, we should keep these happy
+// by using these pointers.
+//
+// When each pin is connected, it will set the corresponding m_pAllocator
+// and will have a single ref-count on that allocator.
+//
+// Refcounts are acquired by GetAllocator calls which return AddReffed
+// allocators and are released in one of:
+// CBaseInputPin::Disconnect
+// CBaseOutputPin::BreakConect
+// In each case m_pAllocator is set to NULL after the release, so this
+// is the last chance to ever release it. If there should ever be
+// multiple refcounts associated with the same pointer, this had better
+// be cleared up before that happens. To avoid such problems, we'll
+// stick with one per pointer.
+
+
+
+// RECONNECTING and STATE CHANGES
+//
+// Each pin could be disconnected, connected with a read-only allocator,
+// connected with an upstream read/write allocator, connected with an
+// allocator from downstream or connected with its own allocator.
+// Five states for each pin gives a data space of 25 states.
+//
+// Notation:
+//
+// R/W == read/write
+// R-O == read-only
+//
+//