From 065d2136557fe7910b3e41c48886d7255c63f3c7 Mon Sep 17 00:00:00 2001 From: Chris Danford Date: Sat, 30 Mar 2002 20:31:28 +0000 Subject: [PATCH] added the DirectShow baseclasses to the project so it doesn't need to be built separately anymore. --- stepmania/src/BaseClasses/Debug/strmbasd.lib | Bin 0 -> 1890222 bytes .../src/BaseClasses/Release/STRMBASE.lib | Bin 0 -> 1067994 bytes stepmania/src/BaseClasses/activex.rcv | 140 + stepmania/src/BaseClasses/activex.ver | 56 + stepmania/src/BaseClasses/amextra.cpp | 111 + stepmania/src/BaseClasses/amextra.h | 56 + stepmania/src/BaseClasses/amfilter.cpp | 5207 +++++++++++++++++ stepmania/src/BaseClasses/amfilter.h | 1589 +++++ stepmania/src/BaseClasses/amvideo.cpp | 275 + stepmania/src/BaseClasses/baseclasses.dsp | 323 + stepmania/src/BaseClasses/baseclasses.dsw | 29 + stepmania/src/BaseClasses/baseclasses.mak | 456 ++ stepmania/src/BaseClasses/cache.h | 74 + stepmania/src/BaseClasses/combase.cpp | 254 + stepmania/src/BaseClasses/combase.h | 319 + stepmania/src/BaseClasses/cprop.cpp | 380 ++ stepmania/src/BaseClasses/cprop.h | 95 + stepmania/src/BaseClasses/ctlutil.cpp | 2378 ++++++++ stepmania/src/BaseClasses/ctlutil.h | 919 +++ stepmania/src/BaseClasses/ddmm.cpp | 126 + stepmania/src/BaseClasses/ddmm.h | 28 + stepmania/src/BaseClasses/dllentry.cpp | 332 ++ stepmania/src/BaseClasses/dllsetup.cpp | 651 +++ stepmania/src/BaseClasses/dllsetup.h | 46 + stepmania/src/BaseClasses/dsschedule.h | 132 + stepmania/src/BaseClasses/fourcc.h | 100 + stepmania/src/BaseClasses/measure.h | 222 + stepmania/src/BaseClasses/msgthrd.h | 120 + stepmania/src/BaseClasses/mtype.cpp | 478 ++ stepmania/src/BaseClasses/mtype.h | 89 + stepmania/src/BaseClasses/outputq.cpp | 788 +++ stepmania/src/BaseClasses/outputq.h | 137 + stepmania/src/BaseClasses/pstream.cpp | 196 + stepmania/src/BaseClasses/pstream.h | 114 + stepmania/src/BaseClasses/pullpin.cpp | 510 ++ stepmania/src/BaseClasses/pullpin.h | 152 + stepmania/src/BaseClasses/readme.txt | 25 + stepmania/src/BaseClasses/refclock.cpp | 308 + stepmania/src/BaseClasses/refclock.h | 169 + stepmania/src/BaseClasses/reftime.h | 116 + stepmania/src/BaseClasses/renbase.cpp | 2833 +++++++++ stepmania/src/BaseClasses/renbase.h | 479 ++ stepmania/src/BaseClasses/schedule.cpp | 265 + stepmania/src/BaseClasses/seekpt.cpp | 83 + stepmania/src/BaseClasses/seekpt.h | 30 + stepmania/src/BaseClasses/source.cpp | 519 ++ stepmania/src/BaseClasses/source.h | 172 + stepmania/src/BaseClasses/streams.h | 172 + stepmania/src/BaseClasses/strmctl.cpp | 386 ++ stepmania/src/BaseClasses/strmctl.h | 158 + stepmania/src/BaseClasses/sysclock.cpp | 74 + stepmania/src/BaseClasses/sysclock.h | 39 + stepmania/src/BaseClasses/transfrm.cpp | 974 +++ stepmania/src/BaseClasses/transfrm.h | 304 + stepmania/src/BaseClasses/transip.cpp | 922 +++ stepmania/src/BaseClasses/transip.h | 246 + stepmania/src/BaseClasses/videoctl.cpp | 653 +++ stepmania/src/BaseClasses/videoctl.h | 178 + stepmania/src/BaseClasses/vtrans.cpp | 468 ++ stepmania/src/BaseClasses/vtrans.h | 143 + stepmania/src/BaseClasses/winctrl.cpp | 1974 +++++++ stepmania/src/BaseClasses/winctrl.h | 224 + stepmania/src/BaseClasses/winutil.cpp | 2694 +++++++++ stepmania/src/BaseClasses/winutil.h | 414 ++ stepmania/src/BaseClasses/wxdebug.cpp | 1310 +++++ stepmania/src/BaseClasses/wxdebug.h | 379 ++ stepmania/src/BaseClasses/wxlist.cpp | 885 +++ stepmania/src/BaseClasses/wxlist.h | 551 ++ stepmania/src/BaseClasses/wxutil.cpp | 1210 ++++ stepmania/src/BaseClasses/wxutil.h | 533 ++ stepmania/src/RageMovieTexture.cpp | 11 +- stepmania/src/RageMovieTexture.h | 7 +- 72 files changed, 36782 insertions(+), 8 deletions(-) create mode 100644 stepmania/src/BaseClasses/Debug/strmbasd.lib create mode 100644 stepmania/src/BaseClasses/Release/STRMBASE.lib create mode 100644 stepmania/src/BaseClasses/activex.rcv create mode 100644 stepmania/src/BaseClasses/activex.ver create mode 100644 stepmania/src/BaseClasses/amextra.cpp create mode 100644 stepmania/src/BaseClasses/amextra.h create mode 100644 stepmania/src/BaseClasses/amfilter.cpp create mode 100644 stepmania/src/BaseClasses/amfilter.h create mode 100644 stepmania/src/BaseClasses/amvideo.cpp create mode 100644 stepmania/src/BaseClasses/baseclasses.dsp create mode 100644 stepmania/src/BaseClasses/baseclasses.dsw create mode 100644 stepmania/src/BaseClasses/baseclasses.mak create mode 100644 stepmania/src/BaseClasses/cache.h create mode 100644 stepmania/src/BaseClasses/combase.cpp create mode 100644 stepmania/src/BaseClasses/combase.h create mode 100644 stepmania/src/BaseClasses/cprop.cpp create mode 100644 stepmania/src/BaseClasses/cprop.h create mode 100644 stepmania/src/BaseClasses/ctlutil.cpp create mode 100644 stepmania/src/BaseClasses/ctlutil.h create mode 100644 stepmania/src/BaseClasses/ddmm.cpp create mode 100644 stepmania/src/BaseClasses/ddmm.h create mode 100644 stepmania/src/BaseClasses/dllentry.cpp create mode 100644 stepmania/src/BaseClasses/dllsetup.cpp create mode 100644 stepmania/src/BaseClasses/dllsetup.h create mode 100644 stepmania/src/BaseClasses/dsschedule.h create mode 100644 stepmania/src/BaseClasses/fourcc.h create mode 100644 stepmania/src/BaseClasses/measure.h create mode 100644 stepmania/src/BaseClasses/msgthrd.h create mode 100644 stepmania/src/BaseClasses/mtype.cpp create mode 100644 stepmania/src/BaseClasses/mtype.h create mode 100644 stepmania/src/BaseClasses/outputq.cpp create mode 100644 stepmania/src/BaseClasses/outputq.h create mode 100644 stepmania/src/BaseClasses/pstream.cpp create mode 100644 stepmania/src/BaseClasses/pstream.h create mode 100644 stepmania/src/BaseClasses/pullpin.cpp create mode 100644 stepmania/src/BaseClasses/pullpin.h create mode 100644 stepmania/src/BaseClasses/readme.txt create mode 100644 stepmania/src/BaseClasses/refclock.cpp create mode 100644 stepmania/src/BaseClasses/refclock.h create mode 100644 stepmania/src/BaseClasses/reftime.h create mode 100644 stepmania/src/BaseClasses/renbase.cpp create mode 100644 stepmania/src/BaseClasses/renbase.h create mode 100644 stepmania/src/BaseClasses/schedule.cpp create mode 100644 stepmania/src/BaseClasses/seekpt.cpp create mode 100644 stepmania/src/BaseClasses/seekpt.h create mode 100644 stepmania/src/BaseClasses/source.cpp create mode 100644 stepmania/src/BaseClasses/source.h create mode 100644 stepmania/src/BaseClasses/streams.h create mode 100644 stepmania/src/BaseClasses/strmctl.cpp create mode 100644 stepmania/src/BaseClasses/strmctl.h create mode 100644 stepmania/src/BaseClasses/sysclock.cpp create mode 100644 stepmania/src/BaseClasses/sysclock.h create mode 100644 stepmania/src/BaseClasses/transfrm.cpp create mode 100644 stepmania/src/BaseClasses/transfrm.h create mode 100644 stepmania/src/BaseClasses/transip.cpp create mode 100644 stepmania/src/BaseClasses/transip.h create mode 100644 stepmania/src/BaseClasses/videoctl.cpp create mode 100644 stepmania/src/BaseClasses/videoctl.h create mode 100644 stepmania/src/BaseClasses/vtrans.cpp create mode 100644 stepmania/src/BaseClasses/vtrans.h create mode 100644 stepmania/src/BaseClasses/winctrl.cpp create mode 100644 stepmania/src/BaseClasses/winctrl.h create mode 100644 stepmania/src/BaseClasses/winutil.cpp create mode 100644 stepmania/src/BaseClasses/winutil.h create mode 100644 stepmania/src/BaseClasses/wxdebug.cpp create mode 100644 stepmania/src/BaseClasses/wxdebug.h create mode 100644 stepmania/src/BaseClasses/wxlist.cpp create mode 100644 stepmania/src/BaseClasses/wxlist.h create mode 100644 stepmania/src/BaseClasses/wxutil.cpp create mode 100644 stepmania/src/BaseClasses/wxutil.h diff --git a/stepmania/src/BaseClasses/Debug/strmbasd.lib b/stepmania/src/BaseClasses/Debug/strmbasd.lib new file mode 100644 index 0000000000000000000000000000000000000000..1de5f0e3cdeea80dec8d9537cace5ef36ff05887 GIT binary patch literal 1890222 zcmeFa%X8z&ksl7PR-?yABhAc8zgcOm^)FVZJ=kPV&nrUlYdlG!`N9w&yGIAZXcA;Y z0|HzC?1v8fH`oq`FLu~rhrjJ~=k*2w!~P@XbE>r=No_Yk88ippKm_+(BqJ_U7|fhX-%} z!=u_?*yn#;)ZYFr|9t0P{G-}$!{@ub|5^L%`g!MPuWG-s&&{9L-uYEO|8Y@!=l|l* z_vX)P_xk*ke^&e6uk-WY|7GoaZ$58$aEA=M`#=75?Kky#?`KbIzmd;B_)oR>zRKs{ z{;c-iZ}{{6M-OU$>7T#gWIbx7r6^;q&|3|E2ck^M(g+WZ<_?2L9lm{_om7KEuXg@8r1G z?+m)_VQ(_4Az|1(8gx!}8m*2d_gACY#b|LMdHelVW6;?>KKWwUIBj>2)5KPzH#j}% zWXUJT{eH7?lBTyCgGTEZG<5p?<1F{2bJXr+sr}wTcK`}gT!XVBMT4`!=}``S*6g;9 z+nrX5<*eK8*j9PgG+G}TK%dr{-@NWE=Bq5DebyeHj8@~;^U>^ToM*qd*dHxM^Dm28 z{o7}~$$3_2XEC}S_gCYaEN9RU>uMeJ(UXnV;giMa=4QO84Nc17;q7!aIUHY1M*Y=d zJi7KdgUR)HI+=kS3u{wa^J`R?rgknb$LDBvn!dZ3-xk1+pZy|-?Jv;A6v1FII{&hm zJX^ZtLBB=D&c@Th?QDE9{*&AB(i6j65IvubXRE>M8{Z~m9L�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 z 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%0A(=Rko8cy#?6HvO2i13)>=uuq7ud@t%VI)S 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%0Dk;>831;nhr>Z=( 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
`NP3N?E0pP1&?S3oT`(2^(HY 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�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 Bd3^u? literal 0 HcmV?d00001 diff --git a/stepmania/src/BaseClasses/activex.rcv b/stepmania/src/BaseClasses/activex.rcv new file mode 100644 index 0000000000..5804e25200 --- /dev/null +++ b/stepmania/src/BaseClasses/activex.rcv @@ -0,0 +1,140 @@ +//------------------------------------------------------------------------------ +// File: Activex.rcv +// +// Desc: DirectShow base classes - this file defines the version resource +// used for the application. +// +// NOTE: All strings MUST have an explicit \0 for termination! +// +// For a complete description of the Version Resource, search the +// Microsoft Developer's Network (MSDN) CD-ROM for 'version resource'.. +// +// Copyright (c) 1992 - 2001, Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef _ACTIVEX_RCV_ +#define _ACTIVEX_RCV_ + +#ifndef WIN32 +#define WIN32 +#endif +#include + +#ifndef _ACTIVEX_VER_ +#include +#endif + +// +// Version flags. +// +// OFFICIAL and FINAL should be defined when appropriate. +// + +#ifndef OFFICIAL +#define VER_PRIVATEBUILD VS_FF_PRIVATEBUILD +#else +#define VER_PRIVATEBUILD 0 +#endif + +#ifndef FINAL +#define VER_PRERELEASE VS_FF_PRERELEASE +#else +#define VER_PRERELEASE 0 +#endif + +#ifdef DEBUG +#define VER_DEBUG VS_FF_DEBUG +#else +#define VER_DEBUG 0 +#endif + +// +// Version definitions +// + +#define VERSION_RES_FLAGSMASK 0x0030003FL +#define VERSION_RES_FLAGS (VER_PRIVATEBUILD|VER_PRERELEASE|VER_DEBUG) + +#ifndef VERSION_RES_OS +#define VERSION_RES_OS VOS__WINDOWS32 +#endif + +#ifndef VERSION_RES_TYPE +#define VERSION_RES_TYPE VFT_DLL +#endif + +#ifndef VERSION_RES_SUBTYPE +#define VERSION_RES_SUBTYPE VFT2_UNKNOWN +#endif + +#define VERSION_RES_LANGUAGE 0x409 + +#ifdef UNICODE +#define VERSION_RES_CHARSET 1200 +#else +#define VERSION_RES_CHARSET 1252 +#endif + +#ifndef VERSION_RES_ACTIVEX +#define VERSION_RES_ACTIVEX "Filter dll\0" +#endif + +#ifdef AMOVIE_SELF_REGISTER +#ifndef OLE_SELF_REGISTER +#define OLE_SELF_REGISTER +#endif +#endif + +#ifdef OLE_SELF_REGISTER +#ifdef AMOVIE_SELF_REGISTER +#define VERSION_RES_SELFREGISTER "AM20\0" +#else +#define VERSION_RES_SELFREGISTER "\0" +#endif +#endif + +// +// Version resource +// + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VERSION_RES_MAJOR_VER, VERSION_RES_MINOR_VER, 0, VERSION_RES_BUILD +PRODUCTVERSION VERSION_RES_MAJOR_VER, VERSION_RES_MINOR_VER, 0, VERSION_RES_BUILD +FILEFLAGSMASK VERSION_RES_FLAGSMASK +FILEFLAGS VERSION_RES_FLAGS +FILEOS VERSION_RES_OS +FILETYPE VERSION_RES_TYPE +FILESUBTYPE VERSION_RES_SUBTYPE +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VERSION_RES_COMPANY_NAME + VALUE "Comment", VERSION_RES_COMMENT + VALUE "FileDescription", VERSION_RES_BIN_DESCRIPTION + VALUE "FileVersion", VERSION_RES_STRING + VALUE "InternalName", VERSION_RES_BIN_NAME + VALUE "LegalCopyright", VERSION_RES_COPYRIGHT + VALUE "OriginalFilename", VERSION_RES_BIN_NAME + VALUE "ProductName", VERSION_RES_PRODUCT_NAME +#ifdef DEBUG + VALUE "ProductVersion", VERSION_RES_STRING_D +#else + VALUE "ProductVersion", VERSION_RES_STRING +#endif + VALUE "ActiveMovie", VERSION_RES_ACTIVEX +#ifdef OLE_SELF_REGISTER + VALUE "OLESelfRegister", VERSION_RES_SELFREGISTER +#endif + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", VERSION_RES_LANGUAGE, VERSION_RES_CHARSET + END +END + +#endif +// _ACTIVEX_RCV_ diff --git a/stepmania/src/BaseClasses/activex.ver b/stepmania/src/BaseClasses/activex.ver new file mode 100644 index 0000000000..2a80cb2b58 --- /dev/null +++ b/stepmania/src/BaseClasses/activex.ver @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// File: Activex.ver +// +// Desc: DirectShow base classes - common versioning information for +// ACTIVEX binaries. +// +// Copyright (c) 1996 - 2001, Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef _ACTIVEX_VER_ +#define _ACTIVEX_VER_ + +// NOTE: all string resources that will be used in ACTIVEX.RCV for the +// version resource information *MUST* have an explicit \0 terminator! + +#define VERSION_RES_MAJOR_VER 8 +#define VERSION_RES_MINOR_VER 1 +#define VERSION_RES_BUILD 0 + +#define VERSION_RES_STRING_D "8.10 (Debug)\0" +#define VERSION_RES_STRING "8.10\0" + +#define VERSION_RES_PRODUCT_NAME "DirectX 8.1 Sample\0" +#define VERSION_RES_COMMENT "DirectShow Sample\0" +#define VERSION_RES_COMPANY_NAME "Microsoft Corporation\0" +#define VERSION_RES_COPYRIGHT "Copyright (C) 1992-2001 Microsoft Corporation\0" + +// The following defines are required on a file-by-file basis +// +// #define VERSION_RES_BIN_NAME "sample.ax\0" +// #define VERSION_RES_BIN_DESCRIPTION "Sample Filter\0" +// +// Also required, if you don't want the defaults, are +// +// #define VERSION_RES_ACTIVEX "Filter dll\0" (the default value) +// +// A string defining the type of component. +// +// #define VERSION_RES_TYPE VFT_DLL (default) +// VFT_APP +// VFT_VXD +// VFT_DRV +// VFT_FONT +// VFT_STATIC_LIB +// VFT_UNKNOWN +// +// #define VERSION_RES_SUBTYPE VFT2_UNKNOWN (default) +// VFT2_DRV_INSTALLABLE +// VFT2_DRV_SOUND +// +// +// See winver.h for further details + +#endif + diff --git a/stepmania/src/BaseClasses/amextra.cpp b/stepmania/src/BaseClasses/amextra.cpp new file mode 100644 index 0000000000..156bbd3772 --- /dev/null +++ b/stepmania/src/BaseClasses/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(TCHAR *pObjectName, + CBaseFilter *pFilter, + CCritSec *pLock, + HRESULT *phr, + LPCWSTR pName) : + CBaseInputPin(pObjectName, pFilter, pLock, phr, pName), + m_bAtEndOfStream(FALSE), + m_bCompleteNotified(FALSE) +{ +} +#ifdef UNICODE +CRenderedInputPin::CRenderedInputPin(CHAR *pObjectName, + CBaseFilter *pFilter, + CCritSec *pLock, + HRESULT *phr, + 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/stepmania/src/BaseClasses/amextra.h b/stepmania/src/BaseClasses/amextra.h new file mode 100644 index 0000000000..b10e22ac41 --- /dev/null +++ b/stepmania/src/BaseClasses/amextra.h @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// 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(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__ + diff --git a/stepmania/src/BaseClasses/amfilter.cpp b/stepmania/src/BaseClasses/amfilter.cpp new file mode 100644 index 0000000000..2865bc75d1 --- /dev/null +++ b/stepmania/src/BaseClasses/amfilter.cpp @@ -0,0 +1,5207 @@ +//------------------------------------------------------------------------------ +// 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 + + + +//===================================================================== +// Helpers +//===================================================================== +STDAPI CreateMemoryAllocator(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( + LPUNKNOWN pAgg, + BOOL bRenderer, + IPin *pPin, + 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(const TCHAR *pName, + LPUNKNOWN pUnk, + 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, + 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(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, 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(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(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, + 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(const TCHAR *pName, + LPUNKNOWN pUnk, + 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) +{ + + ASSERT(pLock != NULL); +} + +/* Passes in a redundant HRESULT argument */ + +CBaseFilter::CBaseFilter(TCHAR *pName, + LPUNKNOWN pUnk, + CCritSec *pLock, + REFCLSID clsid, + 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) +{ + + ASSERT(pLock != NULL); + UNREFERENCED_PARAMETER(phr); +} + +#ifdef UNICODE +CBaseFilter::CBaseFilter(const CHAR *pName, + LPUNKNOWN pUnk, + 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) +{ + + ASSERT(pLock != NULL); +} +CBaseFilter::CBaseFilter(CHAR *pName, + LPUNKNOWN pUnk, + CCritSec *pLock, + REFCLSID clsid, + 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) +{ + + ASSERT(pLock != NULL); + UNREFERENCED_PARAMETER(phr); +} +#endif + +/* Destructor */ + +CBaseFilter::~CBaseFilter() +{ + + // 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(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, 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(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(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); + + // 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; + } + } + } + } + + + 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); + + // 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; + } + } + } + } + + + + 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); + + // 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; + } + } + } + } + + + 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(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, + 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); + ASSERT(pPin != NULL); + + 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(FILTER_INFO * pInfo) +{ + CheckPointer(pInfo,E_POINTER); + ValidateReadWritePtr(pInfo,sizeof(FILTER_INFO)); + + if (m_pName) { + lstrcpynW(pInfo->achName, m_pName, sizeof(pInfo->achName)/sizeof(WCHAR)); + } 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( + IFilterGraph * pGraph, + 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) { + DWORD nameLen = lstrlenW(pName)+1; + m_pName = new WCHAR[nameLen]; + if (m_pName) { + CopyMemory(m_pName, pName, nameLen*sizeof(WCHAR)); + } else { + // !!! error here? + ASSERT(FALSE); + } + } + + + 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( + 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, + 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(CBaseFilter *pFilter, + 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,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(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... + IPin **ppPins, // ...in this array + 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(); + } + + /* Calculate the number of available pins */ + + int cRealPins = min(m_PinCount - m_Position, (int) cPins); + if (cRealPins == 0) { + return S_FALSE; + } + + /* 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 (cRealPins && (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); + + cRealPins--; + + } + } + + 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(CBasePin *pPin, + 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,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(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... + AM_MEDIA_TYPE **ppMediaTypes, // ...in this array + 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 = (AM_MEDIA_TYPE)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(TCHAR *pObjectName, + CBaseFilter *pFilter, + CCritSec *pLock, + HRESULT *phr, + 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 + */ + + ASSERT(pFilter != NULL); + ASSERT(pLock != NULL); + + if (pName) { + DWORD nameLen = lstrlenW(pName)+1; + m_pName = new WCHAR[nameLen]; + if (m_pName) { + CopyMemory(m_pName, pName, nameLen*sizeof(WCHAR)); + } + } + +#ifdef DEBUG + m_cRef = 0; +#endif +} + +#ifdef UNICODE +CBasePin::CBasePin(CHAR *pObjectName, + CBaseFilter *pFilter, + CCritSec *pLock, + HRESULT *phr, + 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 + */ + + ASSERT(pFilter != NULL); + ASSERT(pLock != NULL); + + if (pName) { + DWORD nameLen = lstrlenW(pName)+1; + m_pName = new WCHAR[nameLen]; + if (m_pName) { + CopyMemory(m_pName, pName, nameLen*sizeof(WCHAR)); + } + } + +#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() +{ + + // 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, 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))) { + lstrcpyW(ConnectPinInfo.achName, L"Bad Pin"); + } else { + QueryPinInfoReleaseFilter(ConnectPinInfo); + } + + if (FAILED(pReceivePin->QueryPinInfo(&ReceivePinInfo))) { + lstrcpyW(ReceivePinInfo.achName, 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, + 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() ) ); + + + return hr; + } + + DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Connection succeeded"))); + + + 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, + 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 ((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; + } + + DeleteMediaType(pMediaType); + + 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() ) ); + + + 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; + } + + + return hr; + } + + /* Complete the connection */ + + m_Connected = pConnector; + m_Connected->AddRef(); + hr = SetMediaType(pcmt); + if (SUCCEEDED(hr)) { + hr = CompleteConnect(pConnector); + if (SUCCEEDED(hr)) { + + + 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() ) ); + + + 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 ) ) { + + + // 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; + + + return S_OK; + } else { + // no connection - not an error + + + return S_FALSE; + } +} + + +/* Return an AddRef()'d pointer to the connected pin if there is one */ +STDMETHODIMP +CBasePin::ConnectedTo( + 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; + } +} + +#pragma warning(disable:4702) // Unreachable code at return + +/* Return the media type of the connection */ +STDMETHODIMP +CBasePin::ConnectionMediaType( + 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; + } +} + +#pragma warning(default:4702) // Unreachable code at return + +/* Return information about the filter we are connect to */ + +STDMETHODIMP +CBasePin::QueryPinInfo( + 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) { + lstrcpynW(pInfo->achName, m_pName, sizeof(pInfo->achName)/sizeof(WCHAR)); + } else { + pInfo->achName[0] = L'\0'; + } + + pInfo->dir = m_dir; + + return NOERROR; +} + +STDMETHODIMP +CBasePin::QueryDirection( + 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( + 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( + 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, 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(TCHAR *pObjectName, + CBaseFilter *pFilter, + CCritSec *pLock, + HRESULT *phr, + LPCWSTR pName) : + CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT), + m_pAllocator(NULL), + m_pInputPin(NULL) +{ + ASSERT(pFilter); +} + +#ifdef UNICODE +CBaseOutputPin::CBaseOutputPin(CHAR *pObjectName, + CBaseFilter *pFilter, + CCritSec *pLock, + HRESULT *phr, + 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(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, 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(IMediaSample ** ppSample, + REFERENCE_TIME * pStartTime, + 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; + } + + + 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(TCHAR *pObjectName, + CBaseFilter *pFilter, + CCritSec *pLock, + HRESULT *phr, + 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(CHAR *pObjectName, + CBaseFilter *pFilter, + CCritSec *pLock, + HRESULT *phr, + 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, 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( + 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; + } + + + + /* 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 ( + IMediaSample **pSamples, + long nSamples, + 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); + 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(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(TCHAR *pName, + CBaseAllocator *pAllocator, + HRESULT *phr, + 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 +{ + + /* 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); +} + +#ifdef UNICODE +CMediaSample::CMediaSample(CHAR *pName, + CBaseAllocator *pAllocator, + HRESULT *phr, + 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 +{ + + /* 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() +{ + + if (m_pMediaType) { + DeleteMediaType(m_pMediaType); + } +} + +/* Override this to publicise our interfaces */ + +STDMETHODIMP +CMediaSample::QueryInterface(REFIID riid, void **ppv) +{ + if (riid == IID_IMediaSample || + riid == IID_IMediaSample2 || + riid == IID_IUnknown) { + return GetInterface((IMediaSample *) this, ppv); + } else { + 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(BYTE * ptr, LONG cBytes) +{ + 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(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( + REFERENCE_TIME * pTimeStart, // put time here + 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( + REFERENCE_TIME * pTimeStart, + 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( + LONGLONG * pTimeStart, + 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( + LONGLONG * pTimeStart, + LONGLONG * pTimeEnd +) +{ + if (pTimeStart == NULL) { + ASSERT(pTimeEnd == NULL); + m_dwFlags &= ~Sample_MediaTimeValid; + } else { + 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) { + ASSERT(lActual <= GetSize()); + return VFW_E_BUFFER_OVERFLOW; + } + m_lActual = lActual; + return NOERROR; +} + + +/* These allow for limited format changes in band */ + +STDMETHODIMP +CMediaSample::GetMediaType(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(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, + 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, + 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( + TCHAR *pObjectName, + CBaseFilter *pFilter, + CCritSec *pLock, + HRESULT *phr, + 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( + CHAR *pObjectName, + CBaseFilter *pFilter, + CCritSec *pLock, + HRESULT *phr, + 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, 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(TCHAR *pName, + LPUNKNOWN pUnk, + 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) +{ + + if (bEvent) { + m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); + if (m_hSem == NULL) { + *phr = E_OUTOFMEMORY; + return; + } + } +} + +#ifdef UNICODE +CBaseAllocator::CBaseAllocator(CHAR *pName, + LPUNKNOWN pUnk, + 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) +{ + + 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. + + 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, 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( + ALLOCATOR_PROPERTIES* pRequest, + 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( + 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(IMediaSample **ppBuffer, + REFERENCE_TIME *pStartTime, + 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; + + + return NOERROR; +} + + +/* Final release of a CMediaSample will call this */ + +STDMETHODIMP +CBaseAllocator::ReleaseBuffer(IMediaSample * pSample) +{ + CheckPointer(pSample,E_POINTER); + ValidateReadPtr(pSample,sizeof(IMediaSample)); + + + + 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( + 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 + NotifySample(); + } + + 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(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(LPUNKNOWN pUnk, HRESULT *phr) +{ + CUnknown *pUnkRet = new CMemAllocator(NAME("CMemAllocator"), pUnk, phr); + return pUnkRet; +} + +CMemAllocator::CMemAllocator( + TCHAR *pName, + LPUNKNOWN pUnk, + HRESULT *phr) + : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE), + m_pBuffer(NULL) +{ +} + +#ifdef UNICODE +CMemAllocator::CMemAllocator( + CHAR *pName, + LPUNKNOWN pUnk, + 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( + ALLOCATOR_PROPERTIES* pRequest, + 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(); + } + + /* Compute the aligned size */ + LONG lAlignedSize = m_lSize + m_lPrefix; + if (m_lAlignment > 1) { + LONG lRemainder = lAlignedSize % m_lAlignment; + if (lRemainder != 0) { + lAlignedSize += (m_lAlignment - lRemainder); + } + } + + /* Create the contiguous memory block for the samples + making sure it's properly aligned (64K should be enough!) + */ + ASSERT(lAlignedSize % m_lAlignment == 0); + + m_pBuffer = (PBYTE)VirtualAlloc(NULL, + m_lCount * lAlignedSize, + 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/stepmania/src/BaseClasses/amfilter.h b/stepmania/src/BaseClasses/amfilter.h new file mode 100644 index 0000000000..5d0f224d90 --- /dev/null +++ b/stepmania/src/BaseClasses/amfilter.h @@ -0,0 +1,1589 @@ +//------------------------------------------------------------------------------ +// 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( + 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; + +//private: this should really be private... only the MPEG code +// currently looks at it directly and it should use IsFlushing(). + // 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__ */ + + + diff --git a/stepmania/src/BaseClasses/amvideo.cpp b/stepmania/src/BaseClasses/amvideo.cpp new file mode 100644 index 0000000000..5c2755d61d --- /dev/null +++ b/stepmania/src/BaseClasses/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/stepmania/src/BaseClasses/baseclasses.dsp b/stepmania/src/BaseClasses/baseclasses.dsp new file mode 100644 index 0000000000..481c994778 --- /dev/null +++ b/stepmania/src/BaseClasses/baseclasses.dsp @@ -0,0 +1,323 @@ +# Microsoft Developer Studio Project File - Name="BaseClasses" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=BaseClasses - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "baseclasses.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "baseclasses.mak" CFG="BaseClasses - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "BaseClasses - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "BaseClasses - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "BaseClasses - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /Gi /GX /O2 /I "." /I "..\..\..\..\include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "_WIN32_DCOM" /D WINVER=0x400 /Yu"streams.h" /FD /c +# SUBTRACT CPP /Fr +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" /d "WIN32" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 ..\..\..\..\lib\strmiids.lib /nologo /out:"Release\STRMBASE.lib" /nodefaultlib + +!ELSEIF "$(CFG)" == "BaseClasses - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /Gz /MTd /W3 /Gm /Gi /GX /Zi /Od /I "." /I "..\..\..\..\include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "_WIN32_DCOM" /D "DEBUG" /D WINVER=0x400 /Yu"streams.h" /FD /GZ /c +# SUBTRACT CPP /Fr +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" /d "WIN32" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 ..\..\..\..\lib\strmiids.lib /nologo /out:"debug\strmbasd.lib" /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "BaseClasses - Win32 Release" +# Name "BaseClasses - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\amextra.cpp +# End Source File +# Begin Source File + +SOURCE=.\amfilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\amvideo.cpp +# End Source File +# Begin Source File + +SOURCE=.\combase.cpp +# End Source File +# Begin Source File + +SOURCE=.\cprop.cpp +# End Source File +# Begin Source File + +SOURCE=.\ctlutil.cpp +# End Source File +# Begin Source File + +SOURCE=.\ddmm.cpp +# End Source File +# Begin Source File + +SOURCE=.\dllentry.cpp +# ADD CPP /Yc"streams.h" +# End Source File +# Begin Source File + +SOURCE=.\dllsetup.cpp +# End Source File +# Begin Source File + +SOURCE=.\mtype.cpp +# End Source File +# Begin Source File + +SOURCE=.\outputq.cpp +# End Source File +# Begin Source File + +SOURCE=.\pstream.cpp +# End Source File +# Begin Source File + +SOURCE=.\pullpin.cpp +# End Source File +# Begin Source File + +SOURCE=.\refclock.cpp +# End Source File +# Begin Source File + +SOURCE=.\renbase.cpp +# End Source File +# Begin Source File + +SOURCE=.\schedule.cpp +# End Source File +# Begin Source File + +SOURCE=.\seekpt.cpp +# End Source File +# Begin Source File + +SOURCE=.\source.cpp +# End Source File +# Begin Source File + +SOURCE=.\strmctl.cpp +# End Source File +# Begin Source File + +SOURCE=.\sysclock.cpp +# End Source File +# Begin Source File + +SOURCE=.\transfrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\transip.cpp +# End Source File +# Begin Source File + +SOURCE=.\videoctl.cpp +# End Source File +# Begin Source File + +SOURCE=.\vtrans.cpp +# End Source File +# Begin Source File + +SOURCE=.\winctrl.cpp +# End Source File +# Begin Source File + +SOURCE=.\winutil.cpp +# End Source File +# Begin Source File + +SOURCE=.\wxdebug.cpp +# End Source File +# Begin Source File + +SOURCE=.\wxlist.cpp +# End Source File +# Begin Source File + +SOURCE=.\wxutil.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\activex.rcv +# End Source File +# Begin Source File + +SOURCE=.\activex.ver +# End Source File +# Begin Source File + +SOURCE=.\dllsetup.h +# End Source File +# Begin Source File + +SOURCE=.\dsschedule.h +# End Source File +# Begin Source File + +SOURCE=.\fourcc.h +# End Source File +# Begin Source File + +SOURCE=.\measure.h +# End Source File +# Begin Source File + +SOURCE=.\msgthrd.h +# End Source File +# Begin Source File + +SOURCE=.\mtype.h +# End Source File +# Begin Source File + +SOURCE=.\outputq.h +# End Source File +# Begin Source File + +SOURCE=.\pstream.h +# End Source File +# Begin Source File + +SOURCE=.\pullpin.h +# End Source File +# Begin Source File + +SOURCE=.\refclock.h +# End Source File +# Begin Source File + +SOURCE=.\reftime.h +# End Source File +# Begin Source File + +SOURCE=.\renbase.h +# End Source File +# Begin Source File + +SOURCE=.\seekpt.h +# End Source File +# Begin Source File + +SOURCE=.\source.h +# End Source File +# Begin Source File + +SOURCE=.\streams.h +# End Source File +# Begin Source File + +SOURCE=.\strmctl.h +# End Source File +# Begin Source File + +SOURCE=.\sysclock.h +# End Source File +# Begin Source File + +SOURCE=.\transfrm.h +# End Source File +# Begin Source File + +SOURCE=.\transip.h +# End Source File +# Begin Source File + +SOURCE=.\videoctl.h +# End Source File +# Begin Source File + +SOURCE=.\vtrans.h +# End Source File +# Begin Source File + +SOURCE=.\winctrl.h +# End Source File +# Begin Source File + +SOURCE=.\winutil.h +# End Source File +# Begin Source File + +SOURCE=.\wxdebug.h +# End Source File +# Begin Source File + +SOURCE=.\wxlist.h +# End Source File +# Begin Source File + +SOURCE=.\wxutil.h +# End Source File +# End Group +# End Target +# End Project diff --git a/stepmania/src/BaseClasses/baseclasses.dsw b/stepmania/src/BaseClasses/baseclasses.dsw new file mode 100644 index 0000000000..07cc4e7c69 --- /dev/null +++ b/stepmania/src/BaseClasses/baseclasses.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "BaseClasses"=.\baseclasses.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/stepmania/src/BaseClasses/baseclasses.mak b/stepmania/src/BaseClasses/baseclasses.mak new file mode 100644 index 0000000000..a093efe57a --- /dev/null +++ b/stepmania/src/BaseClasses/baseclasses.mak @@ -0,0 +1,456 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on baseclasses.dsp +!IF "$(CFG)" == "" +CFG=BaseClasses - Win32 Debug +!MESSAGE No configuration specified. Defaulting to BaseClasses - Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "BaseClasses - Win32 Release" && "$(CFG)" != "BaseClasses - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "baseclasses.mak" CFG="BaseClasses - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "BaseClasses - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "BaseClasses - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "BaseClasses - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +ALL : "$(OUTDIR)\STRMBASE.lib" + + +CLEAN : + -@erase "$(INTDIR)\amextra.obj" + -@erase "$(INTDIR)\amfilter.obj" + -@erase "$(INTDIR)\amvideo.obj" + -@erase "$(INTDIR)\baseclasses.pch" + -@erase "$(INTDIR)\combase.obj" + -@erase "$(INTDIR)\cprop.obj" + -@erase "$(INTDIR)\ctlutil.obj" + -@erase "$(INTDIR)\ddmm.obj" + -@erase "$(INTDIR)\dllentry.obj" + -@erase "$(INTDIR)\dllsetup.obj" + -@erase "$(INTDIR)\mtype.obj" + -@erase "$(INTDIR)\outputq.obj" + -@erase "$(INTDIR)\pstream.obj" + -@erase "$(INTDIR)\pullpin.obj" + -@erase "$(INTDIR)\refclock.obj" + -@erase "$(INTDIR)\renbase.obj" + -@erase "$(INTDIR)\schedule.obj" + -@erase "$(INTDIR)\seekpt.obj" + -@erase "$(INTDIR)\source.obj" + -@erase "$(INTDIR)\strmctl.obj" + -@erase "$(INTDIR)\sysclock.obj" + -@erase "$(INTDIR)\transfrm.obj" + -@erase "$(INTDIR)\transip.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\videoctl.obj" + -@erase "$(INTDIR)\vtrans.obj" + -@erase "$(INTDIR)\winctrl.obj" + -@erase "$(INTDIR)\winutil.obj" + -@erase "$(INTDIR)\wxdebug.obj" + -@erase "$(INTDIR)\wxlist.obj" + -@erase "$(INTDIR)\wxutil.obj" + -@erase "$(OUTDIR)\STRMBASE.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MT /W3 /Gi /GX /O2 /I "." /I "..\..\..\..\include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "_WIN32_DCOM" /Fp"$(INTDIR)\baseclasses.pch" /Yu"streams.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +RSC=rc.exe +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\baseclasses.bsc" +BSC32_SBRS= \ + +LIB32=link.exe -lib +LIB32_FLAGS=..\..\..\..\lib\strmiids.lib /nologo /out:"$(OUTDIR)\STRMBASE.lib" /nodefaultlib +LIB32_OBJS= \ + "$(INTDIR)\amextra.obj" \ + "$(INTDIR)\amfilter.obj" \ + "$(INTDIR)\amvideo.obj" \ + "$(INTDIR)\combase.obj" \ + "$(INTDIR)\cprop.obj" \ + "$(INTDIR)\ctlutil.obj" \ + "$(INTDIR)\ddmm.obj" \ + "$(INTDIR)\dllentry.obj" \ + "$(INTDIR)\dllsetup.obj" \ + "$(INTDIR)\mtype.obj" \ + "$(INTDIR)\outputq.obj" \ + "$(INTDIR)\pstream.obj" \ + "$(INTDIR)\pullpin.obj" \ + "$(INTDIR)\refclock.obj" \ + "$(INTDIR)\renbase.obj" \ + "$(INTDIR)\schedule.obj" \ + "$(INTDIR)\seekpt.obj" \ + "$(INTDIR)\source.obj" \ + "$(INTDIR)\strmctl.obj" \ + "$(INTDIR)\sysclock.obj" \ + "$(INTDIR)\transfrm.obj" \ + "$(INTDIR)\transip.obj" \ + "$(INTDIR)\videoctl.obj" \ + "$(INTDIR)\vtrans.obj" \ + "$(INTDIR)\winctrl.obj" \ + "$(INTDIR)\winutil.obj" \ + "$(INTDIR)\wxdebug.obj" \ + "$(INTDIR)\wxlist.obj" \ + "$(INTDIR)\wxutil.obj" + +"$(OUTDIR)\STRMBASE.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ELSEIF "$(CFG)" == "BaseClasses - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +ALL : "$(OUTDIR)\strmbasd.lib" + + +CLEAN : + -@erase "$(INTDIR)\amextra.obj" + -@erase "$(INTDIR)\amfilter.obj" + -@erase "$(INTDIR)\amvideo.obj" + -@erase "$(INTDIR)\baseclasses.pch" + -@erase "$(INTDIR)\combase.obj" + -@erase "$(INTDIR)\cprop.obj" + -@erase "$(INTDIR)\ctlutil.obj" + -@erase "$(INTDIR)\ddmm.obj" + -@erase "$(INTDIR)\dllentry.obj" + -@erase "$(INTDIR)\dllsetup.obj" + -@erase "$(INTDIR)\mtype.obj" + -@erase "$(INTDIR)\outputq.obj" + -@erase "$(INTDIR)\pstream.obj" + -@erase "$(INTDIR)\pullpin.obj" + -@erase "$(INTDIR)\refclock.obj" + -@erase "$(INTDIR)\renbase.obj" + -@erase "$(INTDIR)\schedule.obj" + -@erase "$(INTDIR)\seekpt.obj" + -@erase "$(INTDIR)\source.obj" + -@erase "$(INTDIR)\strmctl.obj" + -@erase "$(INTDIR)\sysclock.obj" + -@erase "$(INTDIR)\transfrm.obj" + -@erase "$(INTDIR)\transip.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(INTDIR)\videoctl.obj" + -@erase "$(INTDIR)\vtrans.obj" + -@erase "$(INTDIR)\winctrl.obj" + -@erase "$(INTDIR)\winutil.obj" + -@erase "$(INTDIR)\wxdebug.obj" + -@erase "$(INTDIR)\wxlist.obj" + -@erase "$(INTDIR)\wxutil.obj" + -@erase "$(OUTDIR)\strmbasd.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /Gz /MTd /W3 /Gm /Gi /GX /Zi /Od /I "." /I "..\..\..\..\include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "_WIN32_DCOM" /D "DEBUG" /Fp"$(INTDIR)\baseclasses.pch" /Yu"streams.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +RSC=rc.exe +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\baseclasses.bsc" +BSC32_SBRS= \ + +LIB32=link.exe -lib +LIB32_FLAGS=..\..\..\..\lib\strmiids.lib /nologo /out:"$(OUTDIR)\strmbasd.lib" /nodefaultlib +LIB32_OBJS= \ + "$(INTDIR)\amextra.obj" \ + "$(INTDIR)\amfilter.obj" \ + "$(INTDIR)\amvideo.obj" \ + "$(INTDIR)\combase.obj" \ + "$(INTDIR)\cprop.obj" \ + "$(INTDIR)\ctlutil.obj" \ + "$(INTDIR)\ddmm.obj" \ + "$(INTDIR)\dllentry.obj" \ + "$(INTDIR)\dllsetup.obj" \ + "$(INTDIR)\mtype.obj" \ + "$(INTDIR)\outputq.obj" \ + "$(INTDIR)\pstream.obj" \ + "$(INTDIR)\pullpin.obj" \ + "$(INTDIR)\refclock.obj" \ + "$(INTDIR)\renbase.obj" \ + "$(INTDIR)\schedule.obj" \ + "$(INTDIR)\seekpt.obj" \ + "$(INTDIR)\source.obj" \ + "$(INTDIR)\strmctl.obj" \ + "$(INTDIR)\sysclock.obj" \ + "$(INTDIR)\transfrm.obj" \ + "$(INTDIR)\transip.obj" \ + "$(INTDIR)\videoctl.obj" \ + "$(INTDIR)\vtrans.obj" \ + "$(INTDIR)\winctrl.obj" \ + "$(INTDIR)\winutil.obj" \ + "$(INTDIR)\wxdebug.obj" \ + "$(INTDIR)\wxlist.obj" \ + "$(INTDIR)\wxutil.obj" + +"$(OUTDIR)\strmbasd.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("baseclasses.dep") +!INCLUDE "baseclasses.dep" +!ELSE +!MESSAGE Warning: cannot find "baseclasses.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "BaseClasses - Win32 Release" || "$(CFG)" == "BaseClasses - Win32 Debug" +SOURCE=.\amextra.cpp + +"$(INTDIR)\amextra.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\amfilter.cpp + +"$(INTDIR)\amfilter.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\amvideo.cpp + +"$(INTDIR)\amvideo.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\combase.cpp + +"$(INTDIR)\combase.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\cprop.cpp + +"$(INTDIR)\cprop.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\ctlutil.cpp + +"$(INTDIR)\ctlutil.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\ddmm.cpp + +"$(INTDIR)\ddmm.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\dllentry.cpp + +!IF "$(CFG)" == "BaseClasses - Win32 Release" + +CPP_SWITCHES=/nologo /MT /W3 /Gi /GX /O2 /I "." /I "..\..\..\..\include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "_WIN32_DCOM" /Fp"$(INTDIR)\baseclasses.pch" /Yc"streams.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +"$(INTDIR)\dllentry.obj" "$(INTDIR)\baseclasses.pch" : $(SOURCE) "$(INTDIR)" + $(CPP) @<< + $(CPP_SWITCHES) $(SOURCE) +<< + + +!ELSEIF "$(CFG)" == "BaseClasses - Win32 Debug" + +CPP_SWITCHES=/nologo /Gz /MTd /W3 /Gm /Gi /GX /Zi /Od /I "." /I "..\..\..\..\include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "_WIN32_DCOM" /D "DEBUG" /Fp"$(INTDIR)\baseclasses.pch" /Yc"streams.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +"$(INTDIR)\dllentry.obj" "$(INTDIR)\baseclasses.pch" : $(SOURCE) "$(INTDIR)" + $(CPP) @<< + $(CPP_SWITCHES) $(SOURCE) +<< + + +!ENDIF + +SOURCE=.\dllsetup.cpp + +"$(INTDIR)\dllsetup.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\mtype.cpp + +"$(INTDIR)\mtype.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\outputq.cpp + +"$(INTDIR)\outputq.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\pstream.cpp + +"$(INTDIR)\pstream.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\pullpin.cpp + +"$(INTDIR)\pullpin.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\refclock.cpp + +"$(INTDIR)\refclock.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\renbase.cpp + +"$(INTDIR)\renbase.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\schedule.cpp + +"$(INTDIR)\schedule.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\seekpt.cpp + +"$(INTDIR)\seekpt.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\source.cpp + +"$(INTDIR)\source.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\strmctl.cpp + +"$(INTDIR)\strmctl.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\sysclock.cpp + +"$(INTDIR)\sysclock.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\transfrm.cpp + +"$(INTDIR)\transfrm.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\transip.cpp + +"$(INTDIR)\transip.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\videoctl.cpp + +"$(INTDIR)\videoctl.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\vtrans.cpp + +"$(INTDIR)\vtrans.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\winctrl.cpp + +"$(INTDIR)\winctrl.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\winutil.cpp + +"$(INTDIR)\winutil.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\wxdebug.cpp + +"$(INTDIR)\wxdebug.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\wxlist.cpp + +"$(INTDIR)\wxlist.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + +SOURCE=.\wxutil.cpp + +"$(INTDIR)\wxutil.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\baseclasses.pch" + + + +!ENDIF + diff --git a/stepmania/src/BaseClasses/cache.h b/stepmania/src/BaseClasses/cache.h new file mode 100644 index 0000000000..404207b06d --- /dev/null +++ b/stepmania/src/BaseClasses/cache.h @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +// 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(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__ */ + diff --git a/stepmania/src/BaseClasses/combase.cpp b/stepmania/src/BaseClasses/combase.cpp new file mode 100644 index 0000000000..536faf28f7 --- /dev/null +++ b/stepmania/src/BaseClasses/combase.cpp @@ -0,0 +1,254 @@ +//------------------------------------------------------------------------------ +// 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(const TCHAR *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(const TCHAR *pName, 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(TCHAR *pName, LPUNKNOWN pUnk,HRESULT *phr) : + CBaseObject(pName), + m_cRef(0), + m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast( static_cast(this) ) ) +{ +} + +#ifdef UNICODE +CUnknown::CUnknown(const CHAR *pName, LPUNKNOWN pUnk) +: CBaseObject(pName), m_cRef(0), + m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast( static_cast(this) ) ) +{ } + +CUnknown::CUnknown(CHAR *pName, LPUNKNOWN pUnk,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, 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 { + return ourmax(ULONG(m_cRef), 1ul); + } +} + + +/* Return an interface pointer to a requesting client + performing a thread safe AddRef as necessary */ + +STDAPI GetInterface(LPUNKNOWN pUnk, 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); + ASSERT(SUCCEEDED(hr)); + ASSERT(pUnknown1); + + hr = pSecond->QueryInterface(IID_IUnknown,(void **) &pUnknown2); + ASSERT(SUCCEEDED(hr)); + ASSERT(pUnknown2); + + /* Release the extra interfaces we hold */ + + pUnknown1->Release(); + pUnknown2->Release(); + return (pUnknown1 == pUnknown2); +} + diff --git a/stepmania/src/BaseClasses/combase.h b/stepmania/src/BaseClasses/combase.h new file mode 100644 index 0000000000..d6197e53f3 --- /dev/null +++ b/stepmania/src/BaseClasses/combase.h @@ -0,0 +1,319 @@ +//------------------------------------------------------------------------------ +// 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(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__ */ + + + + diff --git a/stepmania/src/BaseClasses/cprop.cpp b/stepmania/src/BaseClasses/cprop.cpp new file mode 100644 index 0000000000..f737890afb --- /dev/null +++ b/stepmania/src/BaseClasses/cprop.cpp @@ -0,0 +1,380 @@ +//------------------------------------------------------------------------------ +// 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(TCHAR *pName, // Debug only name + 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(CHAR *pName, // Debug only name + 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 + + if (InterlockedDecrement(&m_cRef) == 0) { + m_cRef++; + SetPageSite(NULL); + SetObjects(0,NULL); + delete this; + return ULONG(0); + } else { + return max(ULONG(m_cRef),1ul); + } +} + + +// Expose our IPropertyPage interface + +STDMETHODIMP +CBasePropertyPage::NonDelegatingQueryInterface(REFIID riid,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(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 = (CBasePropertyPage *) 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,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(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 = (CBasePropertyPage *) 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/stepmania/src/BaseClasses/cprop.h b/stepmania/src/BaseClasses/cprop.h new file mode 100644 index 0000000000..034ebc3bdf --- /dev/null +++ b/stepmania/src/BaseClasses/cprop.h @@ -0,0 +1,95 @@ +//------------------------------------------------------------------------------ +// 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(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__ + diff --git a/stepmania/src/BaseClasses/ctlutil.cpp b/stepmania/src/BaseClasses/ctlutil.cpp new file mode 100644 index 0000000000..8a98c83a08 --- /dev/null +++ b/stepmania/src/BaseClasses/ctlutil.cpp @@ -0,0 +1,2378 @@ +//------------------------------------------------------------------------------ +// 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(UINT * pctinfo) { + CheckPointer(pctinfo,E_POINTER); + ValidateReadWritePtr(pctinfo,sizeof(UINT *)); + *pctinfo = 1; + return S_OK; +} + + +typedef HRESULT(STDAPICALLTYPE *LPLOADTYPELIB)( + const OLECHAR FAR *szFile, + ITypeLib FAR* FAR* pptlib); + +typedef HRESULT(STDAPICALLTYPE *LPLOADREGTYPELIB)(REFGUID rguid, + WORD wVerMajor, + WORD wVerMinor, + LCID lcid, + ITypeLib FAR* FAR* pptlib); + +// attempt to find our type library + +STDMETHODIMP +CBaseDispatch::GetTypeInfo( + REFIID riid, + UINT itinfo, + LCID lcid, + 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; + } + + // 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, + OLECHAR ** rgszNames, + UINT cNames, + LCID lcid, + 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, 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(UINT * pctinfo) { + return m_basedisp.GetTypeInfoCount(pctinfo); +} + + +// attempt to find our type library + +STDMETHODIMP +CMediaControl::GetTypeInfo( + UINT itinfo, + LCID lcid, + ITypeInfo ** pptinfo) { + return m_basedisp.GetTypeInfo(IID_IMediaControl, + itinfo, + lcid, + pptinfo); +} + + +STDMETHODIMP +CMediaControl::GetIDsOfNames( + REFIID riid, + OLECHAR ** rgszNames, + UINT cNames, + LCID lcid, + DISPID * rgdispid) { + return m_basedisp.GetIDsOfNames(IID_IMediaControl, + rgszNames, + cNames, + lcid, + rgdispid); +} + + +STDMETHODIMP +CMediaControl::Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS * pdispparams, + VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, + 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(const TCHAR * name,LPUNKNOWN pUnk) : + CUnknown(name, pUnk) { +} + + +// expose our interfaces IMediaEvent and IUnknown + +STDMETHODIMP +CMediaEvent::NonDelegatingQueryInterface(REFIID riid, 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(UINT * pctinfo) { + return m_basedisp.GetTypeInfoCount(pctinfo); +} + + +// attempt to find our type library + +STDMETHODIMP +CMediaEvent::GetTypeInfo( + UINT itinfo, + LCID lcid, + ITypeInfo ** pptinfo) { + return m_basedisp.GetTypeInfo(IID_IMediaEvent, + itinfo, + lcid, + pptinfo); +} + + +STDMETHODIMP +CMediaEvent::GetIDsOfNames( + REFIID riid, + OLECHAR ** rgszNames, + UINT cNames, + LCID lcid, + DISPID * rgdispid) { + return m_basedisp.GetIDsOfNames(IID_IMediaEvent, + rgszNames, + cNames, + lcid, + rgdispid); +} + + +STDMETHODIMP +CMediaEvent::Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS * pdispparams, + VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, + 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(const TCHAR * name,LPUNKNOWN pUnk) : + CUnknown(name, pUnk) { +} + +CMediaPosition::CMediaPosition(const TCHAR * name, + LPUNKNOWN pUnk, + HRESULT * phr) : + CUnknown(name, pUnk) { + UNREFERENCED_PARAMETER(phr); +} + + +// expose our interfaces IMediaPosition and IUnknown + +STDMETHODIMP +CMediaPosition::NonDelegatingQueryInterface(REFIID riid, 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(UINT * pctinfo) { + return m_basedisp.GetTypeInfoCount(pctinfo); +} + + +// attempt to find our type library + +STDMETHODIMP +CMediaPosition::GetTypeInfo( + UINT itinfo, + LCID lcid, + ITypeInfo ** pptinfo) { + return m_basedisp.GetTypeInfo(IID_IMediaPosition, + itinfo, + lcid, + pptinfo); +} + + +STDMETHODIMP +CMediaPosition::GetIDsOfNames( + REFIID riid, + OLECHAR ** rgszNames, + UINT cNames, + LCID lcid, + DISPID * rgdispid) { + return m_basedisp.GetIDsOfNames(IID_IMediaPosition, + rgszNames, + cNames, + lcid, + rgdispid); +} + + +STDMETHODIMP +CMediaPosition::Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS * pdispparams, + VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, + 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(const TCHAR *pName, + LPUNKNOWN pUnk, + 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,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(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(DWORD * pCaps) { + IMediaSeeking* pMS; + HRESULT hr = GetPeerSeeking(&pMS); + if(FAILED(hr)) { + return hr; + } + + hr = pMS->GetCapabilities(pCaps); + pMS->Release(); + return hr; +} + +STDMETHODIMP +CPosPassThru::CheckCapabilities(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(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(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(LONGLONG * pTarget, const GUID * pTargetFormat, + LONGLONG Source, 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( LONGLONG * pCurrent, DWORD CurrentFlags + , 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(LONGLONG *pCurrent, 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)( 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(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(LONGLONG *pStop) { + return GetSeekingLongLong(&IMediaSeeking::GetStopPosition, pStop);; +} + +STDMETHODIMP +CPosPassThru::GetDuration(LONGLONG *pDuration) { + return GetSeekingLongLong(&IMediaSeeking::GetDuration, pDuration);; +} + + +STDMETHODIMP +CPosPassThru::GetPreroll(LONGLONG *pllPreroll) { + return GetSeekingLongLong(&IMediaSeeking::GetPreroll, pllPreroll);; +} + + +STDMETHODIMP +CPosPassThru::GetAvailable( LONGLONG *pEarliest, 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(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(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(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(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(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(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(LONG *pCanSeekForward) { + IMediaPosition* pMP; + HRESULT hr = GetPeer(&pMP); + if(FAILED(hr)) { + return hr; + } + hr = pMP->CanSeekForward(pCanSeekForward); + pMP->Release(); + return hr; +} + + +STDMETHODIMP +CPosPassThru::CanSeekBackward(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(const TCHAR *pName, + LPUNKNOWN pUnk, + 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(LONGLONG *pStartTime,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( + const TCHAR * pName, + LPUNKNOWN pUnk, + HRESULT* phr, + 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, 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(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(GUID *pFormat) { + CheckPointer(pFormat, E_POINTER); + *pFormat = TIME_FORMAT_MEDIA_TIME; + return S_OK; +} + +HRESULT CSourceSeeking::GetDuration(LONGLONG *pDuration) { + CheckPointer(pDuration, E_POINTER); + CAutoLock lock(m_pLock); + *pDuration = m_rtDuration; + return S_OK; +} + +HRESULT CSourceSeeking::GetStopPosition(LONGLONG *pStop) { + CheckPointer(pStop, E_POINTER); + CAutoLock lock(m_pLock); + *pStop = m_rtStop; + return S_OK; +} + +HRESULT CSourceSeeking::GetCurrentPosition(LONGLONG *pCurrent) { + // GetCurrentPosition is typically supported only in renderers and + // not in source filters. + return E_NOTIMPL; +} + +HRESULT CSourceSeeking::GetCapabilities( DWORD * pCapabilities ) { + CheckPointer(pCapabilities, E_POINTER); + *pCapabilities = m_dwSeekingCaps; + return S_OK; +} + +HRESULT CSourceSeeking::CheckCapabilities( 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( LONGLONG * pTarget, const GUID * pTargetFormat, + LONGLONG Source, 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( LONGLONG * pCurrent, DWORD CurrentFlags + , 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( LONGLONG * pCurrent, LONGLONG * pStop ) { + if(pCurrent) { + *pCurrent = m_rtStart; + } + if(pStop) { + *pStop = m_rtStop; + } + + return S_OK;; +} + + +HRESULT CSourceSeeking::GetAvailable( LONGLONG * pEarliest, 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( double * pdRate) { + CheckPointer(pdRate, E_POINTER); + CAutoLock lock(m_pLock); + *pdRate = m_dRateSeeking; + return S_OK; +} + +HRESULT CSourceSeeking::GetPreroll(LONGLONG *pPreroll) { + CheckPointer(pPreroll, E_POINTER); + *pPreroll = 0; + return S_OK; +} + + + + + +// --- CSourcePosition implementation ---------- + + +CSourcePosition::CSourcePosition(const TCHAR * pName, + LPUNKNOWN pUnk, + HRESULT* phr, + 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(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(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(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(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(LONG *pCanSeekForward) { + CheckPointer(pCanSeekForward,E_POINTER); + *pCanSeekForward = OATRUE; + return S_OK; +} + + +// By default we can seek backwards + +STDMETHODIMP +CSourcePosition::CanSeekBackward(LONG *pCanSeekBackward) { + CheckPointer(pCanSeekBackward,E_POINTER); + *pCanSeekBackward = OATRUE; + return S_OK; +} + + +// --- Implementation of CBasicAudio class ---------- + + +CBasicAudio::CBasicAudio(const TCHAR * pName,LPUNKNOWN punk) : + CUnknown(pName, punk) { +} + +// overriden to publicise our interfaces + +STDMETHODIMP +CBasicAudio::NonDelegatingQueryInterface(REFIID riid, void **ppv) { + ValidateReadWritePtr(ppv,sizeof(PVOID)); + if(riid == IID_IBasicAudio) { + return GetInterface((IBasicAudio *) this, ppv); + } + else { + return CUnknown::NonDelegatingQueryInterface(riid, ppv); + } +} + + +STDMETHODIMP +CBasicAudio::GetTypeInfoCount(UINT * pctinfo) { + return m_basedisp.GetTypeInfoCount(pctinfo); +} + + +STDMETHODIMP +CBasicAudio::GetTypeInfo( + UINT itinfo, + LCID lcid, + ITypeInfo ** pptinfo) { + return m_basedisp.GetTypeInfo(IID_IBasicAudio, + itinfo, + lcid, + pptinfo); +} + + +STDMETHODIMP +CBasicAudio::GetIDsOfNames( + REFIID riid, + OLECHAR ** rgszNames, + UINT cNames, + LCID lcid, + DISPID * rgdispid) { + return m_basedisp.GetIDsOfNames(IID_IBasicAudio, + rgszNames, + cNames, + lcid, + rgdispid); +} + + +STDMETHODIMP +CBasicAudio::Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS * pdispparams, + VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, + 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(const TCHAR * pName,LPUNKNOWN punk) : + CUnknown(pName, punk) { +} + + +// overriden to publicise our interfaces + +STDMETHODIMP +CBaseVideoWindow::NonDelegatingQueryInterface(REFIID riid, void **ppv) { + ValidateReadWritePtr(ppv,sizeof(PVOID)); + if(riid == IID_IVideoWindow) { + return GetInterface((IVideoWindow *) this, ppv); + } + else { + return CUnknown::NonDelegatingQueryInterface(riid, ppv); + } +} + + +STDMETHODIMP +CBaseVideoWindow::GetTypeInfoCount(UINT * pctinfo) { + return m_basedisp.GetTypeInfoCount(pctinfo); +} + + +STDMETHODIMP +CBaseVideoWindow::GetTypeInfo( + UINT itinfo, + LCID lcid, + ITypeInfo ** pptinfo) { + return m_basedisp.GetTypeInfo(IID_IVideoWindow, + itinfo, + lcid, + pptinfo); +} + + +STDMETHODIMP +CBaseVideoWindow::GetIDsOfNames( + REFIID riid, + OLECHAR ** rgszNames, + UINT cNames, + LCID lcid, + DISPID * rgdispid) { + return m_basedisp.GetIDsOfNames(IID_IVideoWindow, + rgszNames, + cNames, + lcid, + rgdispid); +} + + +STDMETHODIMP +CBaseVideoWindow::Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS * pdispparams, + VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, + 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(const TCHAR * pName,LPUNKNOWN punk) : + CUnknown(pName, punk) { +} + + +// overriden to publicise our interfaces + +STDMETHODIMP +CBaseBasicVideo::NonDelegatingQueryInterface(REFIID riid, 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(UINT * pctinfo) { + return m_basedisp.GetTypeInfoCount(pctinfo); +} + + +STDMETHODIMP +CBaseBasicVideo::GetTypeInfo( + UINT itinfo, + LCID lcid, + ITypeInfo ** pptinfo) { + return m_basedisp.GetTypeInfo(IID_IBasicVideo, + itinfo, + lcid, + pptinfo); +} + + +STDMETHODIMP +CBaseBasicVideo::GetIDsOfNames( + REFIID riid, + OLECHAR ** rgszNames, + UINT cNames, + LCID lcid, + DISPID * rgdispid) { + return m_basedisp.GetIDsOfNames(IID_IBasicVideo, + rgszNames, + cNames, + lcid, + rgdispid); +} + + +STDMETHODIMP +CBaseBasicVideo::Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS * pdispparams, + VARIANT * pvarResult, + EXCEPINFO * pexcepinfo, + 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, VARIANT* pArgs, 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++) { + + 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(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; + } + } + } + pDest->bstrVal = pSrc->bstrVal; + 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: + if(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( + CCmdQueue * pQ, + LPUNKNOWN pUnk, + HRESULT * phr, + LPUNKNOWN pUnkExecutor, + REFTIME time, + GUID* iid, + long dispidMethod, + short wFlags, + long nArgs, + VARIANT* pDispParams, + VARIANT* pvarResult, + 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, 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(LONG* pConfidence) { + return E_NOTIMPL; +} + + +STDMETHODIMP +CDeferredCommand::GetHResult(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() : + m_listPresentation(NAME("Presentation time command list")), + m_listStream(NAME("Stream time command list")), + m_evDue(TRUE), // 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( + CDeferredCommand **ppCmd, + LPUNKNOWN pUnk, // this object will execute command + REFTIME time, + GUID* iid, + long dispidMethod, + short wFlags, + long cArgs, + VARIANT* pDispParams, + VARIANT* pvarResult, + 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(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->Get(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(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->Get(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(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 + if(m_listPresentation.GetCount() > 0) { + + POSITION pos = m_listPresentation.GetHeadPosition(); + current = m_listPresentation.Get(pos)->GetTime(); + } + + // if we're running, check the stream times too + if(m_bRunning) { + + CRefTime t; + + if(m_listStream.GetCount() > 0) { + + POSITION pos = m_listStream.GetHeadPosition(); + t = m_listStream.Get(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(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 + + if(m_listPresentation.GetCount() > 0) { + POSITION pos = m_listPresentation.GetHeadPosition(); + pCmd = m_listPresentation.Get(pos); + } + + if(m_bRunning && (m_listStream.GetCount() > 0)) { + POSITION pos = m_listStream.GetHeadPosition(); + CDeferredCommand* pStrm = m_listStream.Get(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, CDeferredCommand**ppCmd) { + CAutoLock lock(&m_Lock); + + CRefTime tStream(rtStream); + + // find the earliest stream and presentation time commands + CDeferredCommand* pStream = NULL; + if(m_listStream.GetCount() > 0) { + POSITION pos = m_listStream.GetHeadPosition(); + pStream = m_listStream.Get(pos); + } + CDeferredCommand* pPresent = NULL; + if(m_listPresentation.GetCount() > 0) { + POSITION pos = m_listPresentation.GetHeadPosition(); + pPresent = m_listPresentation.Get(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)) { + pPresent->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/stepmania/src/BaseClasses/ctlutil.h b/stepmania/src/BaseClasses/ctlutil.h new file mode 100644 index 0000000000..5c69edbfde --- /dev/null +++ b/stepmania/src/BaseClasses/ctlutil.h @@ -0,0 +1,919 @@ +//------------------------------------------------------------------------------ +// 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(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); + 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__ diff --git a/stepmania/src/BaseClasses/ddmm.cpp b/stepmania/src/BaseClasses/ddmm.cpp new file mode 100644 index 0000000000..72afc264a4 --- /dev/null +++ b/stepmania/src/BaseClasses/ddmm.cpp @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// 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(GUID* lpGUID, LPSTR szName, LPSTR szDevice, 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(GUID* lpGUID, LPSTR szName, LPSTR szDevice, 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(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(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/stepmania/src/BaseClasses/ddmm.h b/stepmania/src/BaseClasses/ddmm.h new file mode 100644 index 0000000000..2bb95deb9b --- /dev/null +++ b/stepmania/src/BaseClasses/ddmm.h @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------------ +// 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(LPSTR, PDRAWCREATE, PDRAWENUM); +IDirectDraw * DirectDrawCreateFromDeviceEx(LPSTR, PDRAWCREATE, LPDIRECTDRAWENUMERATEEXA); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/stepmania/src/BaseClasses/dllentry.cpp b/stepmania/src/BaseClasses/dllentry.cpp new file mode 100644 index 0000000000..6b74d2e524 --- /dev/null +++ b/stepmania/src/BaseClasses/dllentry.cpp @@ -0,0 +1,332 @@ +//------------------------------------------------------------------------------ +// File: DllEntry.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 + +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, void ** ppv); + STDMETHODIMP_(ULONG)AddRef(); + STDMETHODIMP_(ULONG)Release(); + + // IClassFactory + STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, 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,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() { + if(--m_cRef == 0) { + delete this; + return 0; + } + else { + return m_cRef; + } +} + +STDMETHODIMP +CClassFactory::CreateInstance( + LPUNKNOWN pUnkOuter, + REFIID riid, + void **pv) { + CheckPointer(pv,E_POINTER) + ValidateReadWritePtr(pv,sizeof(void *)); + + /* Enforce the normal OLE rules regarding interfaces and delegation */ + + if(pUnkOuter != NULL) { + if(IsEqualIID(riid,IID_IUnknown) == FALSE) { + 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) { + if(SUCCEEDED(hr)) { + hr = E_OUTOFMEMORY; + } + return hr; + } + + /* Delete the object if we got a construction error */ + + if(FAILED(hr)) { + delete pObj; + 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 +STDAPI +DllGetClassObject( + REFCLSID rClsID, + REFIID riid, + void **pv) { + 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" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID); + +BOOL WINAPI +DllEntryPoint(HINSTANCE hInstance, ULONG ulReason, LPVOID pv) { +#ifdef DEBUG + extern bool g_fDbgInDllEntryPoint; + g_fDbgInDllEntryPoint = true; +#endif + + switch(ulReason) { + + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstance); +#ifdef DEBUG + DbgInitialise(hInstance); +#endif + + { + // 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++; + } + + DWORD cch = wsprintf(szInfo, TEXT("Executable: %s Pid %x Tid %x. "), + pName, GetCurrentProcessId(), GetCurrentThreadId()); + + wsprintf(szInfo+cch, 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/stepmania/src/BaseClasses/dllsetup.cpp b/stepmania/src/BaseClasses/dllsetup.cpp new file mode 100644 index 0000000000..3a0be6f1ab --- /dev/null +++ b/stepmania/src/BaseClasses/dllsetup.cpp @@ -0,0 +1,651 @@ +//------------------------------------------------------------------------------ +// File: DllSetup.cpp +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#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, LPTSTR 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; + wsprintf(achTemp, TEXT("CLSID\\%ls"), szCLSID); + LONG lreturn = RegCreateKey(HKEY_CLASSES_ROOT + , (LPCTSTR)achTemp + , &hkey); + if(ERROR_SUCCESS != lreturn) { + return AmHresultFromWin32(lreturn); + } + + // set description string + // + + wsprintf(achTemp, TEXT("%ls"), szDescription); + lreturn = RegSetValue(hkey + , (LPCTSTR)NULL + , REG_SZ + , achTemp + , sizeof(achTemp)/sizeof(achTemp[0])); + 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; + + wsprintf(achTemp, TEXT("%ls"), szServerType); + lreturn = RegCreateKey(hkey + , achTemp + , &hsubkey); + if(ERROR_SUCCESS != lreturn) { + RegCloseKey(hkey); + return AmHresultFromWin32(lreturn); + } + + // set Server string + // + wsprintf(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); + } + + wsprintf(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]; + wsprintf(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 + , sizeof(achFileName)/sizeof(TCHAR)); + } + + // + // 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 + , sizeof(achFileName)/sizeof(TCHAR)); + } + + // 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/stepmania/src/BaseClasses/dllsetup.h b/stepmania/src/BaseClasses/dllsetup.h new file mode 100644 index 0000000000..310d6717be --- /dev/null +++ b/stepmania/src/BaseClasses/dllsetup.h @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +// 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, LPTSTR ); + + +STDAPI +AMovieSetupRegisterFilter2( const AMOVIESETUP_FILTER * const psetupdata + , IFilterMapper2 * pIFM2 + , BOOL bRegister ); + diff --git a/stepmania/src/BaseClasses/dsschedule.h b/stepmania/src/BaseClasses/dsschedule.h new file mode 100644 index 0000000000..eddb8cd498 --- /dev/null +++ b/stepmania/src/BaseClasses/dsschedule.h @@ -0,0 +1,132 @@ +//------------------------------------------------------------------------------ +// File: DSSchedule.h (replaces DX8's 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( 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__ diff --git a/stepmania/src/BaseClasses/fourcc.h b/stepmania/src/BaseClasses/fourcc.h new file mode 100644 index 0000000000..5f9d66e120 --- /dev/null +++ b/stepmania/src/BaseClasses/fourcc.h @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------------ +// 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/stepmania/src/BaseClasses/measure.h b/stepmania/src/BaseClasses/measure.h new file mode 100644 index 0000000000..a34fb62a0e --- /dev/null +++ b/stepmania/src/BaseClasses/measure.h @@ -0,0 +1,222 @@ +//------------------------------------------------------------------------------ +// 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(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/stepmania/src/BaseClasses/msgthrd.h b/stepmania/src/BaseClasses/msgthrd.h new file mode 100644 index 0000000000..75ce5cd61d --- /dev/null +++ b/stepmania/src/BaseClasses/msgthrd.h @@ -0,0 +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) 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, 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; +}; + diff --git a/stepmania/src/BaseClasses/mtype.cpp b/stepmania/src/BaseClasses/mtype.cpp new file mode 100644 index 0000000000..a69cac44a5 --- /dev/null +++ b/stepmania/src/BaseClasses/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, HRESULT* phr) +{ + HRESULT hr = CopyMediaType(this, &rt); + if (FAILED(hr) && (NULL != phr)) { + *phr = hr; + } +} + + +CMediaType::CMediaType(const CMediaType& rt, 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) || + (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(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(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(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(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, + 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/stepmania/src/BaseClasses/mtype.h b/stepmania/src/BaseClasses/mtype.h new file mode 100644 index 0000000000..9bd770a4ea --- /dev/null +++ b/stepmania/src/BaseClasses/mtype.h @@ -0,0 +1,89 @@ +//------------------------------------------------------------------------------ +// 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&, 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__ */ + diff --git a/stepmania/src/BaseClasses/outputq.cpp b/stepmania/src/BaseClasses/outputq.cpp new file mode 100644 index 0000000000..c1e0d1c37e --- /dev/null +++ b/stepmania/src/BaseClasses/outputq.cpp @@ -0,0 +1,788 @@ +//------------------------------------------------------------------------------ +// 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 + 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_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(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=0; + LONG lNumberToSend=0; // Local copy + NewSegmentPacket* ppacket=0; + + // + // 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( + IMediaSample **ppSamples, + long nSamples, + long *nSamplesProcessed) { + 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; + for(long 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/stepmania/src/BaseClasses/outputq.h b/stepmania/src/BaseClasses/outputq.h new file mode 100644 index 0000000000..257700147f --- /dev/null +++ b/stepmania/src/BaseClasses/outputq.h @@ -0,0 +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) 1992-2001 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; +}; + diff --git a/stepmania/src/BaseClasses/pstream.cpp b/stepmania/src/BaseClasses/pstream.cpp new file mode 100644 index 0000000000..f7642bf586 --- /dev/null +++ b/stepmania/src/BaseClasses/pstream.cpp @@ -0,0 +1,196 @@ +//------------------------------------------------------------------------------ +// File: PStream.cpp +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include + +#ifdef PERF +#include +#endif +// #include "pstream.h" in streams.h + +// +// Constructor +// +CPersistStream::CPersistStream(IUnknown *punk, 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, 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 + wsprintfW(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, 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/stepmania/src/BaseClasses/pstream.h b/stepmania/src/BaseClasses/pstream.h new file mode 100644 index 0000000000..088dddb1e1 --- /dev/null +++ b/stepmania/src/BaseClasses/pstream.h @@ -0,0 +1,114 @@ +//------------------------------------------------------------------------------ +// 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(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__ diff --git a/stepmania/src/BaseClasses/pullpin.cpp b/stepmania/src/BaseClasses/pullpin.cpp new file mode 100644 index 0000000000..548086f4ec --- /dev/null +++ b/stepmania/src/BaseClasses/pullpin.cpp @@ -0,0 +1,510 @@ +//------------------------------------------------------------------------------ +// 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" + + + +CPullPin::CPullPin() + : m_pReader(NULL), + m_pAlloc(NULL), + m_State(TM_Exit) { + +} + +CPullPin::~CPullPin() { + Disconnect(); +} + +// 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)) { + return(hr); + } + + hr = DecideAllocator(pAlloc, NULL); + if(FAILED(hr)) { + Disconnect(); + return hr; + } + + LONGLONG llTotal, llAvail; + hr = m_pReader->Length(&llTotal, &llAvail); + if(FAILED(hr)) { + Disconnect(); + return hr; + } + + // convert from file position to reference time + m_tDuration = llTotal * UNITS; + m_tStop = m_tDuration; + m_tStart = 0; + m_bSync = bSync; + + return S_OK; +} + +// disconnect any connection made in Connect +HRESULT +CPullPin::Disconnect() { + CAutoLock lock(&m_AccessLock); + + StopThread(); + + 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, + 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(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( + 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; + pSample->GetTime(&t1, &t2); + if(t2 > tStop) { + t2 = tStop; + } + + // adjust times to be relative to (aligned) start time + t1 -= tStart; + t2 -= tStart; + pSample->SetTime(&t1, &t2); + + + 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/stepmania/src/BaseClasses/pullpin.h b/stepmania/src/BaseClasses/pullpin.h new file mode 100644 index 0000000000..3601feb5c1 --- /dev/null +++ b/stepmania/src/BaseClasses/pullpin.h @@ -0,0 +1,152 @@ +//------------------------------------------------------------------------------ +// 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( + 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__ diff --git a/stepmania/src/BaseClasses/readme.txt b/stepmania/src/BaseClasses/readme.txt new file mode 100644 index 0000000000..b7460677eb --- /dev/null +++ b/stepmania/src/BaseClasses/readme.txt @@ -0,0 +1,25 @@ +DirectShow Sample -- Base Classes +--------------------------------- + +The Microsoft DirectShow base classes are a set of C++ classes +and utility functions that you can use to implement DirectShow filters. +For complete documentation of the base classes, see "DirectShow Base Classes" +in the DirectShow Reference section of the DirectX SDK documentation. + +NOTE: The BaseClasses header file schedule.h has been renamed to dsschedule.h +to prevent conflicts with the Platform SDK . Only the refclock.h +header in the BaseClasses directory uses this header, so the impact should +be minimal. + +Building for Windows XP +----------------------- +If you want to target Windows XP specifically to use its new features, +you must set WINVER=0x501 in the BaseClasses project file. You must also +install the Windows XP Platform SDK, however, to ensure that you have the +latest header files. For example, wxutil.cpp uses the new TIME_KILL_SYNCHRONOUS flag +only if (WINVER >= 0x501). This flag is conditionally defined in the Windows XP +Platform SDK in mmsystem.h, but only if WINVER is also set to 0x501 when compiling. + + #if (WINVER >= 0x501) + #define TIME_KILL_SYNCHRONOUS 0x0100 + #endif diff --git a/stepmania/src/BaseClasses/refclock.cpp b/stepmania/src/BaseClasses/refclock.cpp new file mode 100644 index 0000000000..2af26dfaee --- /dev/null +++ b/stepmania/src/BaseClasses/refclock.cpp @@ -0,0 +1,308 @@ +//------------------------------------------------------------------------------ +// File: RefClock.cpp +// +// Desc: DirectShow base classes - implements the IReferenceClock interface. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include +#include + + + +// 'this' used in constructor list +#pragma warning(disable:4355) + + +STDMETHODIMP CBaseReferenceClock::NonDelegatingQueryInterface( + REFIID riid, + void ** ppv) { + HRESULT hr; + + if(riid == IID_IReferenceClock) { + hr = GetInterface((IReferenceClock *) this, ppv); + } + else { + hr = CUnknown::NonDelegatingQueryInterface(riid, ppv); + } + return hr; +} + +CBaseReferenceClock::~CBaseReferenceClock() { + + if(m_TimerResolution) timeEndPeriod(m_TimerResolution); + + 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( TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr, 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) { + + + 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; + } + } + } +} + +STDMETHODIMP CBaseReferenceClock::GetTime(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))); + } + 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 + 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 + 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(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; +} + diff --git a/stepmania/src/BaseClasses/refclock.h b/stepmania/src/BaseClasses/refclock.h new file mode 100644 index 0000000000..f1ec57592d --- /dev/null +++ b/stepmania/src/BaseClasses/refclock.h @@ -0,0 +1,169 @@ +//------------------------------------------------------------------------------ +// 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 "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; +}; + +#endif + diff --git a/stepmania/src/BaseClasses/reftime.h b/stepmania/src/BaseClasses/reftime.h new file mode 100644 index 0000000000..0ed32f6e17 --- /dev/null +++ b/stepmania/src/BaseClasses/reftime.h @@ -0,0 +1,116 @@ +//------------------------------------------------------------------------------ +// 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/stepmania/src/BaseClasses/renbase.cpp b/stepmania/src/BaseClasses/renbase.cpp new file mode 100644 index 0000000000..81cdb60b24 --- /dev/null +++ b/stepmania/src/BaseClasses/renbase.cpp @@ -0,0 +1,2833 @@ +//------------------------------------------------------------------------------ +// 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 + TCHAR *pName, // Debug ONLY description + LPUNKNOWN pUnk, // Aggregated owner object + HRESULT *phr) : // General OLE return code + + CBaseFilter(pName,pUnk,&m_InterfaceLock,RenderClass), + m_evComplete(TRUE), + m_bAbort(FALSE), + m_pPosition(NULL), + m_ThreadSignal(TRUE), + 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) +{ + 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,void **ppv) +{ + CAutoLock cObjectCreationLock(&m_ObjectCreationLock); + if (m_pPosition) { + return m_pPosition->NonDelegatingQueryInterface(riid,ppv); + } + + 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, + GetPin(0)); + 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,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))); + + // Do we have 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 sendmessages + 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() +{ + return 1; +} + + +// 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, IPin **ppPin) +{ + CheckPointer(ppPin,E_POINTER); + + if (0==lstrcmpW(Id,L"In")) { + *ppPin = GetPin(0); + ASSERT(*ppPin); + (*ppPin)->AddRef(); + } 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, + REFERENCE_TIME *pStartTime, + 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, + REFERENCE_TIME *ptrStart, + 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(CBaseRenderer *pRenderer, + HRESULT *phr, + 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(LPWSTR *Id) +{ + CheckPointer(Id,E_POINTER); + + *Id = (LPWSTR)CoTaskMemAlloc(8); + if (*Id == NULL) { + return E_OUTOFMEMORY; + } + lstrcpyW(*Id, L"In"); + 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 + TCHAR *pName, // Debug ONLY description + LPUNKNOWN pUnk, // Aggregated owner object + 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, + REFERENCE_TIME *ptrStart, + 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(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( 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, + 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( 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( 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,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(IFilterGraph *pGraph,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; + QueryInterface(IID_IBaseFilter,(void **) &pFilter); + NotifyEvent(EC_WINDOW_DESTROYED, (LPARAM) pFilter, 0); + pFilter->Release(); + } + 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/stepmania/src/BaseClasses/renbase.h b/stepmania/src/BaseClasses/renbase.h new file mode 100644 index 0000000000..5a6eb3e2da --- /dev/null +++ b/stepmania/src/BaseClasses/renbase.h @@ -0,0 +1,479 @@ +//------------------------------------------------------------------------------ +// 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(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__ + diff --git a/stepmania/src/BaseClasses/schedule.cpp b/stepmania/src/BaseClasses/schedule.cpp new file mode 100644 index 0000000000..975fa20321 --- /dev/null +++ b/stepmania/src/BaseClasses/schedule.cpp @@ -0,0 +1,265 @@ +//------------------------------------------------------------------------------ +// File: Schedule.cpp +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include + +#pragma warning(disable:4706) // C4706: assignment within conditional expression + +// 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( 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( 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/stepmania/src/BaseClasses/seekpt.cpp b/stepmania/src/BaseClasses/seekpt.cpp new file mode 100644 index 0000000000..2f49f6660b --- /dev/null +++ b/stepmania/src/BaseClasses/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(LPUNKNOWN pUnk, HRESULT *phr) +{ + return new CSeekingPassThru(NAME("Seeking PassThru"),pUnk, phr); +} + + +STDMETHODIMP CSeekingPassThru::NonDelegatingQueryInterface(REFIID riid, 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( TCHAR *pName, LPUNKNOWN pUnk, 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/stepmania/src/BaseClasses/seekpt.h b/stepmania/src/BaseClasses/seekpt.h new file mode 100644 index 0000000000..49ba33db3d --- /dev/null +++ b/stepmania/src/BaseClasses/seekpt.h @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// 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(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 diff --git a/stepmania/src/BaseClasses/source.cpp b/stepmania/src/BaseClasses/source.cpp new file mode 100644 index 0000000000..f8cb36360a --- /dev/null +++ b/stepmania/src/BaseClasses/source.cpp @@ -0,0 +1,519 @@ +//------------------------------------------------------------------------------ +// 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(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid) + : CBaseFilter(pName, lpunk, &m_cStateLock, clsid), + m_iPins(0), + m_paStreams(NULL) { +} + +CSource::CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr) + : CBaseFilter(pName, lpunk, &m_cStateLock, clsid), + m_iPins(0), + m_paStreams(NULL) { + UNREFERENCED_PARAMETER(phr); +} + +#ifdef UNICODE +CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid) + : CBaseFilter(pName, lpunk, &m_cStateLock, clsid), + m_iPins(0), + m_paStreams(NULL) { +} + +CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, 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(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(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, 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(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(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(8); + if(*Id==NULL) { + return E_OUTOFMEMORY; + } + IntToWstr(i, *Id); + return NOERROR; +} + + + +// +// CSourceStream::Constructor +// +// increments the number of pins present on the filter +CSourceStream::CSourceStream( + TCHAR *pObjectName, + HRESULT *phr, + CSource *ps, + LPCWSTR pPinName) + : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName), + m_pFilter(ps) { + + *phr = m_pFilter->AddPin(this); +} + +#ifdef UNICODE +CSourceStream::CSourceStream( + char *pObjectName, + HRESULT *phr, + CSource *ps, + 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, 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/stepmania/src/BaseClasses/source.h b/stepmania/src/BaseClasses/source.h new file mode 100644 index 0000000000..2f4634e894 --- /dev/null +++ b/stepmania/src/BaseClasses/source.h @@ -0,0 +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) 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(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__ + diff --git a/stepmania/src/BaseClasses/streams.h b/stepmania/src/BaseClasses/streams.h new file mode 100644 index 0000000000..d8ac319a00 --- /dev/null +++ b/stepmania/src/BaseClasses/streams.h @@ -0,0 +1,172 @@ +//------------------------------------------------------------------------------ +// File: Streams.h +// +// Desc: DirectShow base classes - defines overall streams architecture. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __STREAMS__ +#define __STREAMS__ + +// 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 + +#ifdef _MSC_VER +#if _MSC_VER>=1100 +#define AM_NOVTABLE __declspec(novtable) +#else +#define AM_NOVTABLE +#endif +#endif // MSC_VER + + +#include +#include +#include +#include +#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 + +// +// Modified by Chris to look in local baseclasses directory instead of include path +// +#include "baseclasses/reftime.h" // Helper class for REFERENCE_TIME management +#include "baseclasses/wxdebug.h" // 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 "baseclasses/wxutil.h" // General helper classes for threads etc +#include "baseclasses/combase.h" // Base COM classes to support IUnknown +#include "baseclasses/dllsetup.h" // Filter registration support functions +#include "baseclasses/measure.h" // Performance measurement +#include // Light weight com function prototypes + +#include "baseclasses/cache.h" // Simple cache container class +#include "baseclasses/wxlist.h" // Non MFC generic list class +#include "baseclasses/msgthrd.h" // CMsgThread +#include "baseclasses/mtype.h" // Helper class for managing media types +#include "baseclasses/fourcc.h" // conversions between FOURCCs and GUIDs +#include // generated from control.odl +#include "baseclasses/ctlutil.h" // control interface utility classes +#include // event code definitions +#include "baseclasses/amfilter.h" // Main streams architecture class hierachy +#include "baseclasses/transfrm.h" // Generic transform filter +#include "baseclasses/transip.h" // Generic transform-in-place filter +#include // declaration of type GUIDs and well-known clsids +#include "baseclasses/source.h" // Generic source filter +#include "baseclasses/outputq.h" // Output pin queueing +#include // HRESULT status and error definitions +#include "baseclasses/renbase.h" // Base class for writing ActiveX renderers +#include "baseclasses/winutil.h" // Helps with filters that manage windows +#include "baseclasses/winctrl.h" // Implements the IVideoWindow interface +#include "baseclasses/videoctl.h" // Specifically video related classes +#include "baseclasses/refclock.h" // Base clock class +#include "baseclasses/sysclock.h" // System clock +#include "baseclasses/pstream.h" // IPersistStream helper class +#include "baseclasses/vtrans.h" // Video Transform Filter base class +#include "baseclasses/amextra.h" +#include "baseclasses/cprop.h" // Base property page class +#include "baseclasses/strmctl.h" // 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/stepmania/src/BaseClasses/strmctl.cpp b/stepmania/src/BaseClasses/strmctl.cpp new file mode 100644 index 0000000000..f176c14166 --- /dev/null +++ b/stepmania/src/BaseClasses/strmctl.cpp @@ -0,0 +1,386 @@ +//------------------------------------------------------------------------------ +// File: StrmCtl.cpp +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include +#include + +CBaseStreamControl::CBaseStreamControl() +: m_StreamState(STREAM_FLOWING) +, m_StreamStateOnStop(STREAM_FLOWING) // means no pending stop +, m_tStartTime(MAX_TIME) +, m_tStopTime(MAX_TIME) +, 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(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 +( const REFERENCE_TIME * pSampleStart, const REFERENCE_TIME * pSampleStop ) { + CAutoLock lck(&m_CritSec); + + ASSERT(!m_bIsFlushing); + ASSERT(pSampleStart && pSampleStop); + + // Don't ask how I came up with the code below to handle all 19 cases... + 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(); + } + else { + ExecuteStop(); + ExecuteStart(); + } + + return m_StreamState; // case 1, 2, or 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/stepmania/src/BaseClasses/strmctl.h b/stepmania/src/BaseClasses/strmctl.h new file mode 100644 index 0000000000..edb4d78380 --- /dev/null +++ b/stepmania/src/BaseClasses/strmctl.h @@ -0,0 +1,158 @@ +//------------------------------------------------------------------------------ +// 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( 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 diff --git a/stepmania/src/BaseClasses/sysclock.cpp b/stepmania/src/BaseClasses/sysclock.cpp new file mode 100644 index 0000000000..5a04bf8ed6 --- /dev/null +++ b/stepmania/src/BaseClasses/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(LPUNKNOWN pUnk,HRESULT *phr) +{ + return new CSystemClock(NAME("System reference clock"),pUnk, phr); +} + + +CSystemClock::CSystemClock(TCHAR *pName,LPUNKNOWN pUnk,HRESULT *phr) : + CBaseReferenceClock(pName, pUnk, phr) +{ +} + +STDMETHODIMP CSystemClock::NonDelegatingQueryInterface( + REFIID riid, + 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(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/stepmania/src/BaseClasses/sysclock.h b/stepmania/src/BaseClasses/sysclock.h new file mode 100644 index 0000000000..3fe707b740 --- /dev/null +++ b/stepmania/src/BaseClasses/sysclock.h @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// 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(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__ */ diff --git a/stepmania/src/BaseClasses/transfrm.cpp b/stepmania/src/BaseClasses/transfrm.cpp new file mode 100644 index 0000000000..9e328c9e6e --- /dev/null +++ b/stepmania/src/BaseClasses/transfrm.cpp @@ -0,0 +1,974 @@ +//------------------------------------------------------------------------------ +// 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(TCHAR *pName, + 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(char *pName, + 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, 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, 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( + TCHAR *pObjectName, + CTransformFilter *pTransformFilter, + HRESULT * phr, + LPCWSTR pName) + : CBaseInputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pName) { + DbgLog((LOG_TRACE,2,TEXT("CTransformInputPin::CTransformInputPin"))); + m_pTransformFilter = pTransformFilter; +} + +#ifdef UNICODE +CTransformInputPin::CTransformInputPin( + CHAR *pObjectName, + CTransformFilter *pTransformFilter, + HRESULT * phr, + 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( + TCHAR *pObjectName, + CTransformFilter *pTransformFilter, + HRESULT * phr, + 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( + CHAR *pObjectName, + CTransformFilter *pTransformFilter, + HRESULT * phr, + 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, 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, + ALLOCATOR_PROPERTIES* pProp) { + return m_pTransformFilter->DecideBufferSize(pAllocator, pProp); +} + + + +// return a specific media type indexed by iPosition + +HRESULT +CTransformOutputPin::GetMediaType( + int iPosition, + 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/stepmania/src/BaseClasses/transfrm.h b/stepmania/src/BaseClasses/transfrm.h new file mode 100644 index 0000000000..15cec3765d --- /dev/null +++ b/stepmania/src/BaseClasses/transfrm.h @@ -0,0 +1,304 @@ +//------------------------------------------------------------------------------ +// 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( + 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__ */ + + diff --git a/stepmania/src/BaseClasses/transip.cpp b/stepmania/src/BaseClasses/transip.cpp new file mode 100644 index 0000000000..3e87572891 --- /dev/null +++ b/stepmania/src/BaseClasses/transip.cpp @@ -0,0 +1,922 @@ +//------------------------------------------------------------------------------ +// 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 +// +// +// +// 00 means an unconnected pin. +// <- means using a R/W allocator from the upstream filter +// <= means using a R-O allocator from an upstream filter +// || means using our own (R/W) allocator. +// -> means using a R/W allocator from a downstream filter +// (a R-O allocator from downstream is nonsense, it can't ever work). +// +// +// That makes 25 possible states. Some states are nonsense (two different +// allocators from the same place). These are just an artifact of the notation. +// <= <- Nonsense. +// <- <= Nonsense +// Some states are illegal (the output pin never accepts a R-O allocator): +// 00 <= !! Error !! +// <= <= !! Error !! +// || <= !! Error !! +// -> <= !! Error !! +// Three states appears to be inaccessible: +// -> || Inaccessible +// || -> Inaccessible +// || <- Inaccessible +// Some states only ever occur as intermediates with a pending reconnect which +// is guaranteed to finish in another state. +// -> 00 ?? unstable goes to || 00 +// 00 <- ?? unstable goes to 00 || +// -> <- ?? unstable goes to -> -> +// <- || ?? unstable goes to <- <- +// <- -> ?? unstable goes to <- <- +// And that leaves 11 possible resting states: +// 1 00 00 Nothing connected. +// 2 <- 00 Input pin connected. +// 3 <= 00 Input pin connected using R-O allocator. +// 4 || 00 Needs several state changes to get here. +// 5 00 || Output pin connected using our allocator +// 6 00 -> Downstream only connected +// 7 || || Undesirable but can be forced upon us. +// 8 <= || Copy forced. <= -> is preferable +// 9 <= -> OK - forced to copy. +// 10 <- <- Transform in place (ideal) +// 11 -> -> Transform in place (ideal) +// +// The object of the exercise is to ensure that we finish up in states +// 10 or 11 whenever possible. State 10 is only possible if the upstream +// filter has a R/W allocator (the AVI splitter notoriously +// doesn't) and state 11 is only possible if the downstream filter does +// offer an allocator. +// +// The transition table (entries marked * go via a reconnect) +// +// There are 8 possible transitions: +// A: Connect upstream to filter with R-O allocator that insists on using it. +// B: Connect upstream to filter with R-O allocator but chooses not to use it. +// C: Connect upstream to filter with R/W allocator and insists on using it. +// D: Connect upstream to filter with R/W allocator but chooses not to use it. +// E: Connect downstream to a filter that offers an allocator +// F: Connect downstream to a filter that does not offer an allocator +// G: disconnect upstream +// H: Disconnect downstream +// +// A B C D E F G H +// --------------------------------------------------------- +// 00 00 1 | 3 3 2 2 6 5 . . |1 00 00 +// <- 00 2 | . . . . *10/11 10 1 . |2 <- 00 +// <= 00 3 | . . . . *9/11 *7/8 1 . |3 <= 00 +// || 00 4 | . . . . *8 *7 1 . |4 || 00 +// 00 || 5 | 8 7 *10 7 . . . 1 |5 00 || +// 00 -> 6 | 9 11 *10 11 . . . 1 |6 00 -> +// || || 7 | . . . . . . 5 4 |7 || || +// <= || 8 | . . . . . . 5 3 |8 <= || +// <= -> 9 | . . . . . . 6 3 |9 <= -> +// <- <- 10| . . . . . . *5/6 2 |10 <- <- +// -> -> 11| . . . . . . 6 *2/3 |11 -> -> +// --------------------------------------------------------- +// A B C D E F G H +// +// All these states are accessible without requiring any filter to +// change its behaviour but not all transitions are accessible, for +// instance a transition from state 4 to anywhere other than +// state 8 requires that the upstream filter first offer a R-O allocator +// and then changes its mind and offer R/W. This is NOT allowable - it +// leads to things like the output pin getting a R/W allocator from +// upstream and then the input pin being told it can only have a R-O one. +// Note that you CAN change (say) the upstream filter for a different one, but +// only as a disconnect / connect, not as a Reconnect. (Exercise for +// the reader is to see how you get into state 4). +// +// The reconnection stuff goes as follows (some of the cases shown here as +// "no reconnect" may get one to finalise media type - an old story). +// If there is a reconnect where it says "no reconnect" here then the +// reconnection must not change the allocator choice. +// +// state 2: <- 00 transition E <- <- case C <- <- (no change) +// case D -> <- and then to -> -> +// +// state 2: <- 00 transition F <- <- (no reconnect) +// +// state 3: <= 00 transition E <= -> case A <= -> (no change) +// case B -> -> +// transition F <= || case A <= || (no change) +// case B || || +// +// state 4: || 00 transition E || || case B -> || and then all cases to -> -> +// F || || case B || || (no change) +// +// state 5: 00 || transition A <= || (no reconnect) +// B || || (no reconnect) +// C <- || all cases <- <- +// D || || (unfortunate, but upstream's choice) +// +// state 6: 00 -> transition A <= -> (no reconnect) +// B -> -> (no reconnect) +// C <- -> all cases <- <- +// D -> -> (no reconnect) +// +// state 10:<- <- transition G 00 <- case E 00 -> +// case F 00 || +// +// state 11:-> -> transition H -> 00 case A <= 00 (schizo) +// case B <= 00 +// case C <- 00 (schizo) +// case D <- 00 +// +// The Rules: +// To sort out media types: +// The input is reconnected +// if the input pin is connected and the output pin connects +// The output is reconnected +// If the output pin is connected +// and the input pin connects to a different media type +// +// To sort out allocators: +// The input is reconnected +// if the output disconnects and the input was using a downstream allocator +// The output pin calls SetAllocator to pass on a new allocator +// if the output is connected and +// if the input disconnects and the output was using an upstream allocator +// if the input acquires an allocator different from the output one +// and that new allocator is not R-O +// +// Data is copied (i.e. call getbuffer and copy the data before transforming it) +// if the two allocators are different. + + + +// CHAINS of filters: +// +// We sit between two filters (call them A and Z). We should finish up +// with the same allocator on both of our pins and that should be the +// same one that A and Z would have agreed on if we hadn't been in the +// way. Furthermore, it should not matter how many in-place transforms +// are in the way. Let B, C, D... be in-place transforms ("us"). +// Here's how it goes: +// +// 1. +// A connects to B. They agree on A's allocator. +// A-a->B +// +// 2. +// B connects to C. Same story. There is no point in a reconnect, but +// B will request an input reconnect anyway. +// A-a->B-a->C +// +// 3. +// C connects to Z. +// C insists on using A's allocator, but compromises by requesting a reconnect. +// of C's input. +// A-a->B-?->C-a->Z +// +// We now have pending reconnects on both A--->B and B--->C +// +// 4. +// The A--->B link is reconnected. +// A asks B for an allocator. B sees that it has a downstream connection so +// asks its downstream input pin i.e. C's input pin for an allocator. C sees +// that it too has a downstream connection so asks Z for an allocator. +// +// Even though Z's input pin is connected, it is being asked for an allocator. +// It could refuse, in which case the chain is done and will use A's allocator +// Alternatively, Z may supply one. A chooses either Z's or A's own one. +// B's input pin gets NotifyAllocator called to tell it the decision and it +// propagates this downstream by calling ReceiveAllocator on its output pin +// which calls NotifyAllocator on the next input pin downstream etc. +// If the choice is Z then it goes: +// A-z->B-a->C-a->Z +// A-z->B-z->C-a->Z +// A-z->B-z->C-z->Z +// +// And that's IT!! Any further (essentially spurious) reconnects peter out +// with no change in the chain. + +#include +#include +#include + + +// ================================================================= +// Implements the CTransInPlaceFilter class +// ================================================================= + +CTransInPlaceFilter::CTransInPlaceFilter + ( TCHAR *pName, + LPUNKNOWN pUnk, + REFCLSID clsid, + HRESULT *phr, + bool bModifiesData + ) + : CTransformFilter(pName, pUnk, clsid), + m_bModifiesData(bModifiesData) { +#ifdef PERF + RegisterPerfId(); +#endif // PERF + +} // constructor + +#ifdef UNICODE +CTransInPlaceFilter::CTransInPlaceFilter + ( CHAR *pName, + LPUNKNOWN pUnk, + REFCLSID clsid, + HRESULT *phr, + bool bModifiesData + ) + : CTransformFilter(pName, pUnk, clsid), + m_bModifiesData(bModifiesData) { +#ifdef PERF + RegisterPerfId(); +#endif // PERF + +} // constructor +#endif + +// 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 + +// As soon as any pin is needed we create both (this is different from the +// usual transform filter) because enumerators, allocators etc are passed +// through from one pin to another and it becomes very painful if the other +// pin isn't there. If we fail to create either pin we ensure we fail both. + +CBasePin * +CTransInPlaceFilter::GetPin(int n) { + HRESULT hr = S_OK; + + // Create an input pin if not already done + + if(m_pInput == NULL) { + + m_pInput = new CTransInPlaceInputPin(NAME("TransInPlace input pin") + , this // Owner filter + , &hr // Result code + , L"Input" // Pin name + ); + + // Constructor for CTransInPlaceInputPin can't fail + ASSERT(SUCCEEDED(hr)); + } + + // Create an output pin if not already done + + if(m_pInput!=NULL && m_pOutput == NULL) { + + m_pOutput = new CTransInPlaceOutputPin(NAME("TransInPlace output pin") + , this // Owner filter + , &hr // Result code + , L"Output" // Pin name + ); + + // a failed return code should delete the object + + ASSERT(SUCCEEDED(hr)); + if(m_pOutput == NULL) { + delete m_pInput; + m_pInput = NULL; + } + } + + // Return the appropriate pin + + ASSERT(n>=0 && n<=1); + if(n == 0) { + return m_pInput; + } + else if(n==1) { + return m_pOutput; + } + else { + return NULL; + } + +} // GetPin + + + +// dir is the direction of our pin. +// pReceivePin is the pin we are connecting to. +HRESULT CTransInPlaceFilter::CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin) { + UNREFERENCED_PARAMETER(pReceivePin); + ASSERT(m_pInput); + ASSERT(m_pOutput); + + // if we are not part of a graph, then don't indirect the pointer + // this probably prevents use of the filter without a filtergraph + if(!m_pGraph) { + return VFW_E_NOT_IN_GRAPH; + } + + // Always reconnect the input to account for buffering changes + // + // Because we don't get to suggest a type on ReceiveConnection + // we need another way of making sure the right type gets used. + // + // One way would be to have our EnumMediaTypes return our output + // connection type first but more deterministic and simple is to + // call ReconnectEx passing the type we want to reconnect with + // via the base class ReconeectPin method. + + if(dir == PINDIR_OUTPUT) { + if(m_pInput->IsConnected()) { + return ReconnectPin(m_pInput, &m_pOutput->CurrentMediaType()); + } + return NOERROR; + } + + ASSERT(dir == PINDIR_INPUT); + + // Reconnect output if necessary + + if(m_pOutput->IsConnected()) { + + if(m_pInput->CurrentMediaType() + != m_pOutput->CurrentMediaType()) { + return ReconnectPin(m_pOutput, &m_pInput->CurrentMediaType()); + } + } + return NOERROR; + +} // ComnpleteConnect + + +// +// DecideBufferSize +// +// Tell the output pin's allocator what size buffers we require. +// *pAlloc will be the allocator our output pin is using. +// + +HRESULT CTransInPlaceFilter::DecideBufferSize + ( IMemAllocator *pAlloc + , ALLOCATOR_PROPERTIES *pProperties + ) { + ALLOCATOR_PROPERTIES Request, Actual; + HRESULT hr; + + // If we are connected upstream, get his views + if(m_pInput->IsConnected()) { + // Get the input pin allocator, and get its size and count. + // we don't care about his alignment and prefix. + + hr = InputPin()->PeekAllocator()->GetProperties(&Request); + if(FAILED(hr)) { + // Input connected but with a secretive allocator - enough! + return hr; + } + } + else { + // We're reduced to blind guessing. Let's guess one byte and if + // this isn't enough then when the other pin does get connected + // we can revise it. + ZeroMemory(&Request, sizeof(Request)); + Request.cBuffers = 1; + Request.cbBuffer = 1; + } + + + DbgLog((LOG_MEMORY,1,TEXT("Setting Allocator Requirements"))); + DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d"), + Request.cBuffers, Request.cbBuffer)); + + // Pass the allocator requirements to our output side + // but do a little sanity checking first or we'll just hit + // asserts in the allocator. + + pProperties->cBuffers = Request.cBuffers; + pProperties->cbBuffer = Request.cbBuffer; + if(pProperties->cBuffers<=0) {pProperties->cBuffers = 1; + } + if(pProperties->cbBuffer<=0) {pProperties->cbBuffer = 1; + } + hr = pAlloc->SetProperties(pProperties, &Actual); + + if(FAILED(hr)) { + return hr; + } + + DbgLog((LOG_MEMORY,1,TEXT("Obtained Allocator Requirements"))); + DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d, Alignment %d"), + Actual.cBuffers, Actual.cbBuffer, Actual.cbAlign)); + + // Make sure we got the right alignment and at least the minimum required + + if((Request.cBuffers > Actual.cBuffers) + || (Request.cbBuffer > Actual.cbBuffer) + || (Request.cbAlign > Actual.cbAlign)) { + return E_FAIL; + } + return NOERROR; + +} // DecideBufferSize + +// +// Copy +// +// return a pointer to an identical copy of pSample +IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource) { + IMediaSample * pDest; + + HRESULT hr; + REFERENCE_TIME tStart, tStop; + const BOOL bTime = S_OK == pSource->GetTime(&tStart, &tStop); + + // this may block for an indeterminate amount of time + hr = OutputPin()->PeekAllocator()->GetBuffer(&pDest + , bTime ? &tStart : NULL + , bTime ? &tStop : NULL + , m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0); + + if(FAILED(hr)) { + return NULL; + } + + ASSERT(pDest); + IMediaSample2 *pSample2; + if(SUCCEEDED(pDest->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) { + HRESULT hr = pSample2->SetProperties(FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer), + (PBYTE)m_pInput->SampleProps()); + pSample2->Release(); + if(FAILED(hr)) { + pDest->Release(); + return NULL; + } + } + else { + if(bTime) { + pDest->SetTime(&tStart, &tStop); + } + + if(S_OK == pSource->IsSyncPoint()) { + pDest->SetSyncPoint(TRUE); + } + if(S_OK == pSource->IsDiscontinuity() || m_bSampleSkipped) { + pDest->SetDiscontinuity(TRUE); + } + if(S_OK == pSource->IsPreroll()) { + pDest->SetPreroll(TRUE); + } + + // Copy the media type + AM_MEDIA_TYPE *pMediaType; + if(S_OK == pSource->GetMediaType(&pMediaType)) { + pDest->SetMediaType(pMediaType); + DeleteMediaType(pMediaType); + } + + } + + m_bSampleSkipped = FALSE; + + // Copy the sample media times + REFERENCE_TIME TimeStart, TimeEnd; + if(pSource->GetMediaTime(&TimeStart,&TimeEnd) == NOERROR) { + pDest->SetMediaTime(&TimeStart,&TimeEnd); + } + + // Copy the actual data length and the actual data. + { + const long lDataLength = pSource->GetActualDataLength(); + pDest->SetActualDataLength(lDataLength); + + // Copy the sample data + { + BYTE *pSourceBuffer, *pDestBuffer; + long lSourceSize = pSource->GetSize(); + long lDestSize = pDest->GetSize(); + + ASSERT(lDestSize >= lSourceSize && lDestSize >= lDataLength); + + pSource->GetPointer(&pSourceBuffer); + pDest->GetPointer(&pDestBuffer); + ASSERT(lDestSize == 0 || pSourceBuffer != NULL && pDestBuffer != NULL); + + CopyMemory((PVOID) pDestBuffer, (PVOID) pSourceBuffer, lDataLength); + } + } + + return pDest; + +} // Copy + + +// override this to customize the transform process + +HRESULT +CTransInPlaceFilter::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->Deliver(pSample); + } + HRESULT hr; + + // Start timing the TransInPlace (if PERF is defined) + MSR_START(m_idTransInPlace); + + if(UsingDifferentAllocators()) { + + // We have to copy the data. + + pSample = Copy(pSample); + + if(pSample==NULL) { + MSR_STOP(m_idTransInPlace); + return E_UNEXPECTED; + } + } + + // have the derived class transform the data + hr = Transform(pSample); + + // Stop the clock and log it (if PERF is defined) + MSR_STOP(m_idTransInPlace); + + if(FAILED(hr)) { + DbgLog((LOG_TRACE, 1, TEXT("Error from TransInPlace"))); + if(UsingDifferentAllocators()) { + pSample->Release(); + } + return hr; + } + + // 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->Deliver(pSample); + } + else { + // But it would be an error to return this private workaround + // to the caller ... + if(S_FALSE == hr) { + // 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. + m_bSampleSkipped = TRUE; + if(!m_bQualityChanged) { + NotifyEvent(EC_QUALITY_CHANGE,0,0); + m_bQualityChanged = TRUE; + } + hr = NOERROR; + } + } + + // release the output buffer. If the connected pin still needs it, + // it will have addrefed it itself. + if(UsingDifferentAllocators()) { + pSample->Release(); + } + + return hr; + +} // Receive + + + +// ================================================================= +// Implements the CTransInPlaceInputPin class +// ================================================================= + + +// constructor + +CTransInPlaceInputPin::CTransInPlaceInputPin + ( TCHAR *pObjectName + , CTransInPlaceFilter *pFilter + , HRESULT *phr + , LPCWSTR pName + ) + : CTransformInputPin(pObjectName, + pFilter, + phr, + pName) + , m_bReadOnly(FALSE) + , m_pTIPFilter(pFilter) { + + DbgLog((LOG_TRACE, 2 + , TEXT("CTransInPlaceInputPin::CTransInPlaceInputPin"))); + +} // constructor + + +// ================================================================= +// Implements IMemInputPin interface +// ================================================================= + + +// If the downstream filter has one then offer that (even if our own output +// pin is not using it yet. If the upstream filter chooses it then we will +// tell our output pin to ReceiveAllocator). +// Else if our output pin is using an allocator then offer that. +// ( This could mean offering the upstream filter his own allocator, +// it could mean offerring our own +// ) or it could mean offering the one from downstream +// Else fail to offer any allocator at all. + +STDMETHODIMP CTransInPlaceInputPin::GetAllocator(IMemAllocator ** ppAllocator) { + CheckPointer(ppAllocator,E_POINTER); + ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *)); + CAutoLock cObjectLock(m_pLock); + + HRESULT hr; + + if(m_pTIPFilter->m_pOutput->IsConnected()) { + // Store the allocator we got + hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin() + ->GetAllocator(ppAllocator); + if(SUCCEEDED(hr)) { + m_pTIPFilter->OutputPin()->SetAllocator(*ppAllocator); + } + } + else { + // Help upstream filter (eg TIP filter which is having to do a copy) + // by providing a temp allocator here - we'll never use + // this allocator because when our output is connected we'll + // reconnect this pin + hr = CTransformInputPin::GetAllocator(ppAllocator); + } + return hr; + +} // GetAllocator + + + +/* Get told which allocator the upstream output pin is actually going to use */ + + +STDMETHODIMP +CTransInPlaceInputPin::NotifyAllocator( + IMemAllocator * pAllocator, + BOOL bReadOnly) { + HRESULT hr = S_OK; + CheckPointer(pAllocator,E_POINTER); + ValidateReadPtr(pAllocator,sizeof(IMemAllocator)); + + CAutoLock cObjectLock(m_pLock); + + m_bReadOnly = bReadOnly; + // If we modify data then don't accept the allocator if it's + // the same as the output pin's allocator + + // If our output is not connected just accept the allocator + // We're never going to use this allocator because when our + // output pin is connected we'll reconnect this pin + if(!m_pTIPFilter->OutputPin()->IsConnected()) { + return CTransformInputPin::NotifyAllocator(pAllocator, bReadOnly); + } + + // If the allocator is read-only and we're modifying data + // and the allocator is the same as the output pin's + // then reject + if(bReadOnly && m_pTIPFilter->m_bModifiesData) { + IMemAllocator *pOutputAllocator = + m_pTIPFilter->OutputPin()->PeekAllocator(); + + // Make sure we have an output allocator + if(pOutputAllocator == NULL) { + hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()-> + GetAllocator(&pOutputAllocator); + if(FAILED(hr)) { + hr = CreateMemoryAllocator(&pOutputAllocator); + } + if(SUCCEEDED(hr)) { + m_pTIPFilter->OutputPin()->SetAllocator(pOutputAllocator); + pOutputAllocator->Release(); + } + } + if(pAllocator == pOutputAllocator) { + hr = E_FAIL; + } + else if(SUCCEEDED(hr)) { + // Must copy so set the allocator properties on the output + ALLOCATOR_PROPERTIES Props, Actual={0}; + hr = pAllocator->GetProperties(&Props); + if(SUCCEEDED(hr)) { + hr = pOutputAllocator->SetProperties(&Props, &Actual); + } + if(SUCCEEDED(hr)) { + if((Props.cBuffers > Actual.cBuffers) + || (Props.cbBuffer > Actual.cbBuffer) + || (Props.cbAlign > Actual.cbAlign)) { + hr = E_FAIL; + } + } + + // Set the allocator on the output pin + if(SUCCEEDED(hr)) { + hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin() + ->NotifyAllocator(pOutputAllocator, FALSE); + } + } + } + else { + hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin() + ->NotifyAllocator(pAllocator, bReadOnly); + if(SUCCEEDED(hr)) { + m_pTIPFilter->OutputPin()->SetAllocator(pAllocator); + } + } + + if(SUCCEEDED(hr)) { + + // It's possible that the old and the new are the same thing. + // AddRef before release ensures that we don't unload it. + pAllocator->AddRef(); + + if(m_pAllocator != NULL) + m_pAllocator->Release(); + + m_pAllocator = pAllocator; // We have an allocator for the input pin + } + + return hr; + +} // NotifyAllocator + + +// EnumMediaTypes +// - pass through to our downstream filter +STDMETHODIMP CTransInPlaceInputPin::EnumMediaTypes( IEnumMediaTypes **ppEnum ) { + // Can only pass through if connected + if(!m_pTIPFilter->m_pOutput->IsConnected()) + return VFW_E_NOT_CONNECTED; + + return m_pTIPFilter->m_pOutput->GetConnected()->EnumMediaTypes(ppEnum); + +} // EnumMediaTypes + + +// CheckMediaType +// - agree to anything if not connected, +// otherwise pass through to the downstream filter. +// This assumes that the filter does not change the media type. + +HRESULT CTransInPlaceInputPin::CheckMediaType(const CMediaType *pmt ) { + HRESULT hr = m_pTIPFilter->CheckInputType(pmt); + if(hr!=S_OK) return hr; + + if(m_pTIPFilter->m_pOutput->IsConnected()) + return m_pTIPFilter->m_pOutput->GetConnected()->QueryAccept(pmt); + else + return S_OK; + +} // CheckMediaType + + +// If upstream asks us what our requirements are, we will try to ask downstream +// if that doesn't work, we'll just take the defaults. +STDMETHODIMP +CTransInPlaceInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps) { + + if(m_pTIPFilter->m_pOutput->IsConnected()) + return m_pTIPFilter->OutputPin() + ->ConnectedIMemInputPin()->GetAllocatorRequirements(pProps); + else + return E_NOTIMPL; + +} // GetAllocatorRequirements + + + +// ================================================================= +// Implements the CTransInPlaceOutputPin class +// ================================================================= + + +// constructor + +CTransInPlaceOutputPin::CTransInPlaceOutputPin( + TCHAR *pObjectName, + CTransInPlaceFilter *pFilter, + HRESULT * phr, + LPCWSTR pPinName) + : CTransformOutputPin( pObjectName + , pFilter + , phr + , pPinName), + m_pTIPFilter(pFilter) { + + DbgLog(( LOG_TRACE, 2 + , TEXT("CTransInPlaceOutputPin::CTransInPlaceOutputPin"))); + +} // constructor + + +// EnumMediaTypes +// - pass through to our upstream filter +STDMETHODIMP CTransInPlaceOutputPin::EnumMediaTypes( IEnumMediaTypes **ppEnum ) { + // Can only pass through if connected. + if(! m_pTIPFilter->m_pInput->IsConnected()) + return VFW_E_NOT_CONNECTED; + + return m_pTIPFilter->m_pInput->GetConnected()->EnumMediaTypes(ppEnum); + +} // EnumMediaTypes + + + +// CheckMediaType +// - agree to anything if not connected, +// otherwise pass through to the upstream filter. + +HRESULT CTransInPlaceOutputPin::CheckMediaType(const CMediaType *pmt ) { + // Don't accept any output pin type changes if we're copying + // between allocators - it's too late to change the input + // allocator size. + if(m_pTIPFilter->UsingDifferentAllocators() && !m_pFilter->IsStopped()) { + if(*pmt == m_mt) { + return S_OK; + } + else { + return VFW_E_TYPE_NOT_ACCEPTED; + } + } + + // Assumes the type does not change. That's why we're calling + // CheckINPUTType here on the OUTPUT pin. + HRESULT hr = m_pTIPFilter->CheckInputType(pmt); + if(hr!=S_OK) + return hr; + + if(m_pTIPFilter->m_pInput->IsConnected()) + return m_pTIPFilter->m_pInput->GetConnected()->QueryAccept(pmt); + else + return S_OK; + +} //CheckMediaType + + +/* Save the allocator pointer in the output pin +*/ +void +CTransInPlaceOutputPin::SetAllocator(IMemAllocator * pAllocator) { + pAllocator->AddRef(); + if(m_pAllocator) { + m_pAllocator->Release(); + } + m_pAllocator = pAllocator; +} // receiveAllocator diff --git a/stepmania/src/BaseClasses/transip.h b/stepmania/src/BaseClasses/transip.h new file mode 100644 index 0000000000..f36a1c4295 --- /dev/null +++ b/stepmania/src/BaseClasses/transip.h @@ -0,0 +1,246 @@ +//------------------------------------------------------------------------------ +// File: TransIP.h +// +// Desc: DirectShow base classes - defines classes from which simple +// Transform-In-Place filters may be derived. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// +// The difference between this and Transfrm.h is that Transfrm copies the data. +// +// It assumes the filter 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 __TRANSIP__ +#define __TRANSIP__ + +// ====================================================================== +// This is the com object that represents a simple transform filter. It +// supports IBaseFilter, IMediaFilter and two pins through nested interfaces +// ====================================================================== + +class CTransInPlaceFilter; + +// Several of the pin functions call filter functions to do the work, +// so you can often use the pin classes unaltered, just overriding the +// functions in CTransInPlaceFilter. If that's not enough and you want +// to derive your own pin class, override GetPin in the filter to supply +// your own pin classes to the filter. + +// ================================================== +// Implements the input pin +// ================================================== + +class CTransInPlaceInputPin : public CTransformInputPin +{ + +protected: + CTransInPlaceFilter * const m_pTIPFilter; // our filter + BOOL m_bReadOnly; // incoming stream is read only + +public: + + CTransInPlaceInputPin( + TCHAR *pObjectName, + CTransInPlaceFilter *pFilter, + HRESULT *phr, + LPCWSTR pName); + + // --- IMemInputPin ----- + + // Provide an enumerator for media types by getting one from downstream + STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum ); + + // Say whether media type is acceptable. + HRESULT CheckMediaType(const CMediaType* pmt); + + // Return our upstream allocator + STDMETHODIMP GetAllocator(IMemAllocator ** ppAllocator); + + // get told which allocator the upstream output pin is actually + // going to use. + STDMETHODIMP NotifyAllocator(IMemAllocator * pAllocator, + BOOL bReadOnly); + + // Allow the filter to see what allocator we have + // N.B. This does NOT AddRef + IMemAllocator * PeekAllocator() const + { return m_pAllocator; } + + // Pass this on downstream if it ever gets called. + STDMETHODIMP + CTransInPlaceInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps); + + inline const BOOL ReadOnly() { return m_bReadOnly ; } + +}; // CTransInPlaceInputPin + +// ================================================== +// Implements the output pin +// ================================================== + +class CTransInPlaceOutputPin : public CTransformOutputPin +{ + +protected: + // m_pFilter points to our CBaseFilter + CTransInPlaceFilter * const m_pTIPFilter; + +public: + + CTransInPlaceOutputPin( + TCHAR *pObjectName, + CTransInPlaceFilter *pFilter, + HRESULT *phr, + LPCWSTR pName); + + + // --- CBaseOutputPin ------------ + + // negotiate the allocator and its buffer size/count + // Insists on using our own allocator. (Actually the one upstream of us). + // We don't override this - instead we just agree the default + // then let the upstream filter decide for itself on reconnect + // virtual HRESULT DecideAllocator(IMemInputPin * pPin, IMemAllocator ** pAlloc); + + // Provide a media type enumerator. Get it from upstream. + STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum ); + + // Say whether media type is acceptable. + HRESULT CheckMediaType(const CMediaType* pmt); + + // This just saves the allocator being used on the output pin + // Also called by input pin's GetAllocator() + void SetAllocator(IMemAllocator * pAllocator); + + IMemInputPin * ConnectedIMemInputPin() + { return m_pInputPin; } + + // Allow the filter to see what allocator we have + // N.B. This does NOT AddRef + IMemAllocator * PeekAllocator() const + { return m_pAllocator; } +}; // CTransInPlaceOutputPin + + +class AM_NOVTABLE CTransInPlaceFilter : public CTransformFilter +{ + +public: + + // map getpin/getpincount for base enum of pins to owner + // override this to return more specialised pin objects + + virtual CBasePin *GetPin(int n); + +public: + + // Set bModifiesData == false if your derived filter does + // not modify the data samples (for instance it's just copying + // them somewhere else or looking at the timestamps). + + CTransInPlaceFilter(TCHAR *, LPUNKNOWN, REFCLSID clsid, HRESULT *, + bool bModifiesData = true); +#ifdef UNICODE + CTransInPlaceFilter(CHAR *, LPUNKNOWN, REFCLSID clsid, HRESULT *, + bool bModifiesData = true); +#endif + // The following are defined to avoid undefined pure virtuals. + // Even if they are never called, they will give linkage warnings/errors + + // We override EnumMediaTypes to bypass the transform class enumerator + // which would otherwise call this. + HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) + { DbgBreak("CTransInPlaceFilter::GetMediaType should never be called"); + return E_UNEXPECTED; + } + + // This is called when we actually have to provide out own allocator. + HRESULT DecideBufferSize(IMemAllocator*, ALLOCATOR_PROPERTIES *); + + // The functions which call this in CTransform are overridden in this + // class to call CheckInputType with the assumption that the type + // does not change. In Debug builds some calls will be made and + // we just ensure that they do not assert. + HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut) + { + return S_OK; + }; + + + // ================================================================= + // ----- You may want to override this ----------------------------- + // ================================================================= + + HRESULT CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin); + + // chance to customize the transform process + virtual HRESULT Receive(IMediaSample *pSample); + + // ================================================================= + // ----- You MUST override these ----------------------------------- + // ================================================================= + + virtual HRESULT Transform(IMediaSample *pSample) PURE; + + // this goes in the factory template table to create new instances + // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *); + + +#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_idTransInPlace = MSR_REGISTER(TEXT("TransInPlace"));} +#endif // PERF + + +// implementation details + +protected: + + IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource); + +#ifdef PERF + int m_idTransInPlace; // performance measuring id +#endif // PERF + bool m_bModifiesData; // Does this filter change the data? + + // these hold our input and output pins + + friend class CTransInPlaceInputPin; + friend class CTransInPlaceOutputPin; + + CTransInPlaceInputPin *InputPin() const + { + return (CTransInPlaceInputPin *)m_pInput; + }; + CTransInPlaceOutputPin *OutputPin() const + { + return (CTransInPlaceOutputPin *)m_pOutput; + }; + + // Helper to see if the input and output types match + BOOL TypesMatch() + { + return InputPin()->CurrentMediaType() == + OutputPin()->CurrentMediaType(); + } + + // Are the input and output allocators different? + BOOL UsingDifferentAllocators() const + { + return InputPin()->PeekAllocator() != OutputPin()->PeekAllocator(); + } +}; // CTransInPlaceFilter + +#endif /* __TRANSIP__ */ + diff --git a/stepmania/src/BaseClasses/videoctl.cpp b/stepmania/src/BaseClasses/videoctl.cpp new file mode 100644 index 0000000000..ca00ce1752 --- /dev/null +++ b/stepmania/src/BaseClasses/videoctl.cpp @@ -0,0 +1,653 @@ +//------------------------------------------------------------------------------ +// File: VideoCtl.cpp +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include +#include "ddmm.h" + +// Load a string from the resource file string table. The buffer must be at +// least STR_MAX_LENGTH bytes. The easiest way to use this is to declare a +// buffer in the property page class and use it for all string loading. It +// cannot be static as multiple property pages may be active simultaneously + +TCHAR *WINAPI StringFromResource(TCHAR *pBuffer, int iResourceID) { + if(LoadString(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH) == 0) { + return TEXT(""); + } + return pBuffer; +} + +#ifdef UNICODE +char *WINAPI StringFromResource(char *pBuffer, int iResourceID) { + if(LoadStringA(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH) == 0) { + return ""; + } + return pBuffer; +} +#endif + + + +// Property pages typically are called through their OLE interfaces. These +// use UNICODE strings regardless of how the binary is built. So when we +// load strings from the resource file we sometimes want to convert them +// to UNICODE. This method is passed the target UNICODE buffer and does a +// convert after loading the string (if built UNICODE this is not needed) +// On WinNT we can explicitly call LoadStringW which saves two conversions + +#ifndef UNICODE + +WCHAR * WINAPI WideStringFromResource(WCHAR *pBuffer, int iResourceID) { + *pBuffer = 0; + + if(g_amPlatform == VER_PLATFORM_WIN32_NT) { + LoadStringW(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH); + } + else { + + CHAR szBuffer[STR_MAX_LENGTH]; + DWORD dwStringLength = LoadString(g_hInst,iResourceID,szBuffer,STR_MAX_LENGTH); + // if we loaded a string convert it to wide characters, ensuring + // that we also null terminate the result. + if(dwStringLength++) { + MultiByteToWideChar(CP_ACP,0,szBuffer,dwStringLength,pBuffer,STR_MAX_LENGTH); + } + } + return pBuffer; +} + +#endif + + +// Helper function to calculate the size of the dialog + +BOOL WINAPI GetDialogSize(int iResourceID, + DLGPROC pDlgProc, + LPARAM lParam, + SIZE *pResult) { + RECT rc; + HWND hwnd; + + // Create a temporary property page + + hwnd = CreateDialogParam(g_hInst, + MAKEINTRESOURCE(iResourceID), + GetDesktopWindow(), + pDlgProc, + lParam); + if(hwnd == NULL) { + return FALSE; + } + + GetWindowRect(hwnd, &rc); + pResult->cx = rc.right - rc.left; + pResult->cy = rc.bottom - rc.top; + + DestroyWindow(hwnd); + return TRUE; +} + + +// Class that aggregates on the IDirectDraw interface. Although DirectDraw +// has the ability in its interfaces to be aggregated they're not currently +// implemented. This makes it difficult for various parts of Quartz that want +// to aggregate these interfaces. In particular the video renderer passes out +// media samples that expose IDirectDraw and IDirectDrawSurface. The filter +// graph manager also exposes IDirectDraw as a plug in distributor. For these +// objects we provide these aggregation classes that republish the interfaces + +STDMETHODIMP CAggDirectDraw::NonDelegatingQueryInterface(REFIID riid, void **ppv) { + ASSERT(m_pDirectDraw); + + // Do we have this interface + + if(riid == IID_IDirectDraw) { + return GetInterface((IDirectDraw *)this,ppv); + } + else { + return CUnknown::NonDelegatingQueryInterface(riid,ppv); + } +} + + +STDMETHODIMP CAggDirectDraw::Compact() { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->Compact(); +} + + +STDMETHODIMP CAggDirectDraw::CreateClipper(DWORD dwFlags,LPDIRECTDRAWCLIPPER *lplpDDClipper,IUnknown *pUnkOuter) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->CreateClipper(dwFlags,lplpDDClipper,pUnkOuter); +} + + +STDMETHODIMP CAggDirectDraw::CreatePalette(DWORD dwFlags,LPPALETTEENTRY lpColorTable,LPDIRECTDRAWPALETTE *lplpDDPalette,IUnknown *pUnkOuter) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->CreatePalette(dwFlags,lpColorTable,lplpDDPalette,pUnkOuter); +} + + +STDMETHODIMP CAggDirectDraw::CreateSurface(LPDDSURFACEDESC lpDDSurfaceDesc,LPDIRECTDRAWSURFACE *lplpDDSurface,IUnknown *pUnkOuter) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->CreateSurface(lpDDSurfaceDesc,lplpDDSurface,pUnkOuter); +} + + +STDMETHODIMP CAggDirectDraw::DuplicateSurface(LPDIRECTDRAWSURFACE lpDDSurface,LPDIRECTDRAWSURFACE *lplpDupDDSurface) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->DuplicateSurface(lpDDSurface,lplpDupDDSurface); +} + + +STDMETHODIMP CAggDirectDraw::EnumDisplayModes(DWORD dwSurfaceDescCount,LPDDSURFACEDESC lplpDDSurfaceDescList,LPVOID lpContext,LPDDENUMMODESCALLBACK lpEnumCallback) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->EnumDisplayModes(dwSurfaceDescCount,lplpDDSurfaceDescList,lpContext,lpEnumCallback); +} + + +STDMETHODIMP CAggDirectDraw::EnumSurfaces(DWORD dwFlags,LPDDSURFACEDESC lpDDSD,LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpEnumCallback) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->EnumSurfaces(dwFlags,lpDDSD,lpContext,lpEnumCallback); +} + + +STDMETHODIMP CAggDirectDraw::FlipToGDISurface() { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->FlipToGDISurface(); +} + + +STDMETHODIMP CAggDirectDraw::GetCaps(LPDDCAPS lpDDDriverCaps,LPDDCAPS lpDDHELCaps) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetCaps(lpDDDriverCaps,lpDDHELCaps); +} + + +STDMETHODIMP CAggDirectDraw::GetDisplayMode(LPDDSURFACEDESC lpDDSurfaceDesc) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetDisplayMode(lpDDSurfaceDesc); +} + + +STDMETHODIMP CAggDirectDraw::GetFourCCCodes(LPDWORD lpNumCodes,LPDWORD lpCodes) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetFourCCCodes(lpNumCodes,lpCodes); +} + + +STDMETHODIMP CAggDirectDraw::GetGDISurface(LPDIRECTDRAWSURFACE *lplpGDIDDSurface) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetGDISurface(lplpGDIDDSurface); +} + + +STDMETHODIMP CAggDirectDraw::GetMonitorFrequency(LPDWORD lpdwFrequency) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetMonitorFrequency(lpdwFrequency); +} + + +STDMETHODIMP CAggDirectDraw::GetScanLine(LPDWORD lpdwScanLine) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetScanLine(lpdwScanLine); +} + + +STDMETHODIMP CAggDirectDraw::GetVerticalBlankStatus(LPBOOL lpblsInVB) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetVerticalBlankStatus(lpblsInVB); +} + + +STDMETHODIMP CAggDirectDraw::Initialize(GUID *lpGUID) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->Initialize(lpGUID); +} + + +STDMETHODIMP CAggDirectDraw::RestoreDisplayMode() { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->RestoreDisplayMode(); +} + + +STDMETHODIMP CAggDirectDraw::SetCooperativeLevel(HWND hWnd,DWORD dwFlags) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->SetCooperativeLevel(hWnd,dwFlags); +} + + +STDMETHODIMP CAggDirectDraw::SetDisplayMode(DWORD dwWidth,DWORD dwHeight,DWORD dwBpp) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->SetDisplayMode(dwWidth,dwHeight,dwBpp); +} + + +STDMETHODIMP CAggDirectDraw::WaitForVerticalBlank(DWORD dwFlags,HANDLE hEvent) { + ASSERT(m_pDirectDraw); + return m_pDirectDraw->WaitForVerticalBlank(dwFlags,hEvent); +} + + +// Class that aggregates an IDirectDrawSurface interface. Although DirectDraw +// has the ability in its interfaces to be aggregated they're not currently +// implemented. This makes it difficult for various parts of Quartz that want +// to aggregate these interfaces. In particular the video renderer passes out +// media samples that expose IDirectDraw and IDirectDrawSurface. The filter +// graph manager also exposes IDirectDraw as a plug in distributor. For these +// objects we provide these aggregation classes that republish the interfaces + +STDMETHODIMP CAggDrawSurface::NonDelegatingQueryInterface(REFIID riid, void **ppv) { + ASSERT(m_pDirectDrawSurface); + + // Do we have this interface + + if(riid == IID_IDirectDrawSurface) { + return GetInterface((IDirectDrawSurface *)this,ppv); + } + else { + return CUnknown::NonDelegatingQueryInterface(riid,ppv); + } +} + + +STDMETHODIMP CAggDrawSurface::AddAttachedSurface(LPDIRECTDRAWSURFACE lpDDSAttachedSurface) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->AddAttachedSurface(lpDDSAttachedSurface); +} + + +STDMETHODIMP CAggDrawSurface::AddOverlayDirtyRect(LPRECT lpRect) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->AddOverlayDirtyRect(lpRect); +} + + +STDMETHODIMP CAggDrawSurface::Blt(LPRECT lpDestRect,LPDIRECTDRAWSURFACE lpDDSrcSurface,LPRECT lpSrcRect,DWORD dwFlags,LPDDBLTFX lpDDBltFx) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Blt(lpDestRect,lpDDSrcSurface,lpSrcRect,dwFlags,lpDDBltFx); +} + + +STDMETHODIMP CAggDrawSurface::BltBatch(LPDDBLTBATCH lpDDBltBatch,DWORD dwCount,DWORD dwFlags) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->BltBatch(lpDDBltBatch,dwCount,dwFlags); +} + + +STDMETHODIMP CAggDrawSurface::BltFast(DWORD dwX,DWORD dwY,LPDIRECTDRAWSURFACE lpDDSrcSurface,LPRECT lpSrcRect,DWORD dwTrans) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->BltFast(dwX,dwY,lpDDSrcSurface,lpSrcRect,dwTrans); +} + + +STDMETHODIMP CAggDrawSurface::DeleteAttachedSurface(DWORD dwFlags,LPDIRECTDRAWSURFACE lpDDSAttachedSurface) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->DeleteAttachedSurface(dwFlags,lpDDSAttachedSurface); +} + + +STDMETHODIMP CAggDrawSurface::EnumAttachedSurfaces(LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->EnumAttachedSurfaces(lpContext,lpEnumSurfacesCallback); +} + + +STDMETHODIMP CAggDrawSurface::EnumOverlayZOrders(DWORD dwFlags,LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpfnCallback) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->EnumOverlayZOrders(dwFlags,lpContext,lpfnCallback); +} + + +STDMETHODIMP CAggDrawSurface::Flip(LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride,DWORD dwFlags) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Flip(lpDDSurfaceTargetOverride,dwFlags); +} + + +STDMETHODIMP CAggDrawSurface::GetAttachedSurface(LPDDSCAPS lpDDSCaps,LPDIRECTDRAWSURFACE *lplpDDAttachedSurface) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetAttachedSurface(lpDDSCaps,lplpDDAttachedSurface); +} + + +STDMETHODIMP CAggDrawSurface::GetBltStatus(DWORD dwFlags) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetBltStatus(dwFlags); +} + + +STDMETHODIMP CAggDrawSurface::GetCaps(LPDDSCAPS lpDDSCaps) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetCaps(lpDDSCaps); +} + + +STDMETHODIMP CAggDrawSurface::GetClipper(LPDIRECTDRAWCLIPPER *lplpDDClipper) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetClipper(lplpDDClipper); +} + + +STDMETHODIMP CAggDrawSurface::GetColorKey(DWORD dwFlags,LPDDCOLORKEY lpDDColorKey) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetColorKey(dwFlags,lpDDColorKey); +} + + +STDMETHODIMP CAggDrawSurface::GetDC(HDC *lphDC) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetDC(lphDC); +} + + +STDMETHODIMP CAggDrawSurface::GetFlipStatus(DWORD dwFlags) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetFlipStatus(dwFlags); +} + + +STDMETHODIMP CAggDrawSurface::GetOverlayPosition(LPLONG lpdwX,LPLONG lpdwY) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetOverlayPosition(lpdwX,lpdwY); +} + + +STDMETHODIMP CAggDrawSurface::GetPalette(LPDIRECTDRAWPALETTE *lplpDDPalette) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetPalette(lplpDDPalette); +} + + +STDMETHODIMP CAggDrawSurface::GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetPixelFormat(lpDDPixelFormat); +} + + +// A bit of a warning here: Our media samples in DirectShow aggregate on +// IDirectDraw and IDirectDrawSurface (ie are available through IMediaSample +// by QueryInterface). Unfortunately the underlying DirectDraw code cannot +// be aggregated so we have to use these classes. The snag is that when we +// call a different surface and pass in this interface as perhaps the source +// surface the call will fail because DirectDraw dereferences the pointer to +// get at its private data structures. Therefore we supply this workaround to give +// access to the real IDirectDraw surface. A filter can call GetSurfaceDesc +// and we will fill in the lpSurface pointer with the real underlying surface + +STDMETHODIMP CAggDrawSurface::GetSurfaceDesc(LPDDSURFACEDESC lpDDSurfaceDesc) { + ASSERT(m_pDirectDrawSurface); + + // First call down to the underlying DirectDraw + + HRESULT hr = m_pDirectDrawSurface->GetSurfaceDesc(lpDDSurfaceDesc); + if(FAILED(hr)) { + return hr; + } + + // Store the real DirectDrawSurface interface + lpDDSurfaceDesc->lpSurface = m_pDirectDrawSurface; + return hr; +} + + +STDMETHODIMP CAggDrawSurface::Initialize(LPDIRECTDRAW lpDD,LPDDSURFACEDESC lpDDSurfaceDesc) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Initialize(lpDD,lpDDSurfaceDesc); +} + + +STDMETHODIMP CAggDrawSurface::IsLost() { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->IsLost(); +} + + +STDMETHODIMP CAggDrawSurface::Lock(LPRECT lpDestRect,LPDDSURFACEDESC lpDDSurfaceDesc,DWORD dwFlags,HANDLE hEvent) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Lock(lpDestRect,lpDDSurfaceDesc,dwFlags,hEvent); +} + + +STDMETHODIMP CAggDrawSurface::ReleaseDC(HDC hDC) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->ReleaseDC(hDC); +} + + +STDMETHODIMP CAggDrawSurface::Restore() { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Restore(); +} + + +STDMETHODIMP CAggDrawSurface::SetClipper(LPDIRECTDRAWCLIPPER lpDDClipper) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->SetClipper(lpDDClipper); +} + + +STDMETHODIMP CAggDrawSurface::SetColorKey(DWORD dwFlags,LPDDCOLORKEY lpDDColorKey) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->SetColorKey(dwFlags,lpDDColorKey); +} + + +STDMETHODIMP CAggDrawSurface::SetOverlayPosition(LONG dwX,LONG dwY) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->SetOverlayPosition(dwX,dwY); +} + + +STDMETHODIMP CAggDrawSurface::SetPalette(LPDIRECTDRAWPALETTE lpDDPalette) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->SetPalette(lpDDPalette); +} + + +STDMETHODIMP CAggDrawSurface::Unlock(LPVOID lpSurfaceData) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Unlock(lpSurfaceData); +} + + +STDMETHODIMP CAggDrawSurface::UpdateOverlay(LPRECT lpSrcRect,LPDIRECTDRAWSURFACE lpDDDestSurface,LPRECT lpDestRect,DWORD dwFlags,LPDDOVERLAYFX lpDDOverlayFX) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->UpdateOverlay(lpSrcRect,lpDDDestSurface,lpDestRect,dwFlags,lpDDOverlayFX); +} + + +STDMETHODIMP CAggDrawSurface::UpdateOverlayDisplay(DWORD dwFlags) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->UpdateOverlayDisplay(dwFlags); +} + + +STDMETHODIMP CAggDrawSurface::UpdateOverlayZOrder(DWORD dwFlags,LPDIRECTDRAWSURFACE lpDDSReference) { + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->UpdateOverlayZOrder(dwFlags,lpDDSReference); +} + + +// DirectShow must work on multiple platforms. In particular, it also runs on +// Windows NT 3.51 which does not have DirectDraw capabilities. The filters +// cannot therefore link statically to the DirectDraw library. To make their +// lives that little bit easier we provide this class that manages loading +// and unloading the library and creating the initial IDirectDraw interface + +CLoadDirectDraw::CLoadDirectDraw() : + m_pDirectDraw(NULL), + m_hDirectDraw(NULL) { +} + + +// Destructor forces unload + +CLoadDirectDraw::~CLoadDirectDraw() { + ReleaseDirectDraw(); + + if(m_hDirectDraw) { + NOTE("Unloading library"); + FreeLibrary(m_hDirectDraw); + } +} + + +// We can't be sure that DirectDraw is always available so we can't statically +// link to the library. Therefore we load the library, get the function entry +// point addresses and call them to create the driver objects. We return S_OK +// if we manage to load DirectDraw correctly otherwise we return E_NOINTERFACE +// We initialise a DirectDraw instance by explicitely loading the library and +// calling GetProcAddress on the DirectDrawCreate entry point that it exports + +// On a multi monitor system, we can get the DirectDraw object for any +// monitor (device) with the optional szDevice parameter + +HRESULT CLoadDirectDraw::LoadDirectDraw(LPSTR szDevice) { + PDRAWCREATE pDrawCreate; + PDRAWENUM pDrawEnum; + LPDIRECTDRAWENUMERATEEXA pDrawEnumEx; + HRESULT hr = NOERROR; + + NOTE("Entering DoLoadDirectDraw"); + + // Is DirectDraw already loaded + + if(m_pDirectDraw) { + NOTE("Already loaded"); + ASSERT(m_hDirectDraw); + return NOERROR; + } + + // Make sure the library is available + + if(!m_hDirectDraw) { + UINT ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX); + m_hDirectDraw = LoadLibrary(TEXT("DDRAW.DLL")); + SetErrorMode(ErrorMode); + + if(m_hDirectDraw == NULL) { + DbgLog((LOG_ERROR,1,TEXT("Can't load DDRAW.DLL"))); + NOTE("No library"); + return E_NOINTERFACE; + } + } + + // Get the DLL address for the creator function + + pDrawCreate = (PDRAWCREATE)GetProcAddress(m_hDirectDraw,"DirectDrawCreate"); + // force ANSI, we assume it + pDrawEnum = (PDRAWENUM)GetProcAddress(m_hDirectDraw,"DirectDrawEnumerateA"); + pDrawEnumEx = (LPDIRECTDRAWENUMERATEEXA)GetProcAddress(m_hDirectDraw, + "DirectDrawEnumerateExA"); + + // We don't NEED DirectDrawEnumerateEx, that's just for multimon stuff + if(pDrawCreate == NULL || pDrawEnum == NULL) { + DbgLog((LOG_ERROR,1,TEXT("Can't get functions: Create=%x Enum=%x"), + pDrawCreate, pDrawEnum)); + NOTE("No entry point"); + ReleaseDirectDraw(); + return E_NOINTERFACE; + } + + DbgLog((LOG_TRACE,3,TEXT("Creating DDraw for device %s"), + szDevice ? szDevice : "")); + + // Create a DirectDraw display provider for this device, using the fancy + // multimon-aware version, if it exists + if(pDrawEnumEx) + m_pDirectDraw = DirectDrawCreateFromDeviceEx(szDevice, pDrawCreate, + pDrawEnumEx); + else + m_pDirectDraw = DirectDrawCreateFromDevice(szDevice, pDrawCreate, + pDrawEnum); + + if(m_pDirectDraw == NULL) { + DbgLog((LOG_ERROR,1,TEXT("Can't create DDraw"))); + NOTE("No instance"); + ReleaseDirectDraw(); + return E_NOINTERFACE; + } + return NOERROR; +} + + +// Called to release any DirectDraw provider we previously loaded. We may be +// called at any time especially when something goes horribly wrong and when +// we need to clean up before returning so we can't guarantee that all state +// variables are consistent so free only those really allocated allocated +// This should only be called once all reference counts have been released + +void CLoadDirectDraw::ReleaseDirectDraw() { + NOTE("Releasing DirectDraw driver"); + + // Release any DirectDraw provider interface + + if(m_pDirectDraw) { + NOTE("Releasing instance"); + m_pDirectDraw->Release(); + m_pDirectDraw = NULL; + } + +} + + +// Return NOERROR (S_OK) if DirectDraw has been loaded by this object + +HRESULT CLoadDirectDraw::IsDirectDrawLoaded() { + NOTE("Entering IsDirectDrawLoaded"); + + if(m_pDirectDraw == NULL) { + NOTE("DirectDraw not loaded"); + return S_FALSE; + } + return NOERROR; +} + + +// Return the IDirectDraw interface we look after + +LPDIRECTDRAW CLoadDirectDraw::GetDirectDraw() { + NOTE("Entering GetDirectDraw"); + + if(m_pDirectDraw == NULL) { + NOTE("No DirectDraw"); + return NULL; + } + + NOTE("Returning DirectDraw"); + m_pDirectDraw->AddRef(); + return m_pDirectDraw; +} + + +// Are we running on Direct Draw version 1? We need to find out as +// we rely on specific bug fixes in DirectDraw 2 for fullscreen playback. To +// find out, we simply see if it supports IDirectDraw2. Only version 2 and +// higher support this. + +BOOL CLoadDirectDraw::IsDirectDrawVersion1() { + + if(m_pDirectDraw == NULL) + return FALSE; + + IDirectDraw2 *p = NULL; + HRESULT hr = m_pDirectDraw->QueryInterface(IID_IDirectDraw2, (void **)&p); + if(p) + p->Release(); + if(hr == NOERROR) { + DbgLog((LOG_TRACE,3,TEXT("Direct Draw Version 2 or greater"))); + return FALSE; + } + else { + DbgLog((LOG_TRACE,3,TEXT("Direct Draw Version 1"))); + return TRUE; + } +} + diff --git a/stepmania/src/BaseClasses/videoctl.h b/stepmania/src/BaseClasses/videoctl.h new file mode 100644 index 0000000000..cf2a5e4b2b --- /dev/null +++ b/stepmania/src/BaseClasses/videoctl.h @@ -0,0 +1,178 @@ +//------------------------------------------------------------------------------ +// File: VideoCtl.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __VIDEOCTL__ +#define __VIDEOCTL__ + +// These help with property page implementations. The first can be used to +// load any string from a resource file. The buffer to load into is passed +// as an input parameter. The same buffer is the return value if the string +// was found otherwise it returns TEXT(""). The GetDialogSize is passed the +// resource ID of a dialog box and returns the size of it in screen pixels + +#define STR_MAX_LENGTH 256 +TCHAR * WINAPI StringFromResource(TCHAR *pBuffer, int iResourceID); + +#ifdef UNICODE +#define WideStringFromResource StringFromResource +char* WINAPI StringFromResource(char*pBuffer, int iResourceID); +#else +WCHAR * WINAPI WideStringFromResource(WCHAR *pBuffer, int iResourceID); +#endif + + +BOOL WINAPI GetDialogSize(int iResourceID, // Dialog box resource identifier + DLGPROC pDlgProc, // Pointer to dialog procedure + LPARAM lParam, // Any user data wanted in pDlgProc + SIZE *pResult); // Returns the size of dialog box + +// Class that aggregates an IDirectDraw interface + +class CAggDirectDraw : public IDirectDraw, public CUnknown +{ +protected: + + LPDIRECTDRAW m_pDirectDraw; + +public: + + DECLARE_IUNKNOWN + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv); + + // Constructor and destructor + + CAggDirectDraw(TCHAR *pName,LPUNKNOWN pUnk) : + CUnknown(pName,pUnk), + m_pDirectDraw(NULL) { }; + + virtual CAggDirectDraw::~CAggDirectDraw() { }; + + // Set the object we should be aggregating + void SetDirectDraw(LPDIRECTDRAW pDirectDraw) { + m_pDirectDraw = pDirectDraw; + } + + // IDirectDraw methods + + STDMETHODIMP Compact(); + STDMETHODIMP CreateClipper(DWORD dwFlags,LPDIRECTDRAWCLIPPER *lplpDDClipper,IUnknown *pUnkOuter); + STDMETHODIMP CreatePalette(DWORD dwFlags,LPPALETTEENTRY lpColorTable,LPDIRECTDRAWPALETTE *lplpDDPalette,IUnknown *pUnkOuter); + STDMETHODIMP CreateSurface(LPDDSURFACEDESC lpDDSurfaceDesc,LPDIRECTDRAWSURFACE *lplpDDSurface,IUnknown *pUnkOuter); + STDMETHODIMP DuplicateSurface(LPDIRECTDRAWSURFACE lpDDSurface,LPDIRECTDRAWSURFACE *lplpDupDDSurface); + STDMETHODIMP EnumDisplayModes(DWORD dwSurfaceDescCount,LPDDSURFACEDESC lplpDDSurfaceDescList,LPVOID lpContext,LPDDENUMMODESCALLBACK lpEnumCallback); + STDMETHODIMP EnumSurfaces(DWORD dwFlags,LPDDSURFACEDESC lpDDSD,LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpEnumCallback); + STDMETHODIMP FlipToGDISurface(); + STDMETHODIMP GetCaps(LPDDCAPS lpDDDriverCaps,LPDDCAPS lpDDHELCaps); + STDMETHODIMP GetDisplayMode(LPDDSURFACEDESC lpDDSurfaceDesc); + STDMETHODIMP GetFourCCCodes(LPDWORD lpNumCodes,LPDWORD lpCodes); + STDMETHODIMP GetGDISurface(LPDIRECTDRAWSURFACE *lplpGDIDDSurface); + STDMETHODIMP GetMonitorFrequency(LPDWORD lpdwFrequency); + STDMETHODIMP GetScanLine(LPDWORD lpdwScanLine); + STDMETHODIMP GetVerticalBlankStatus(LPBOOL lpblsInVB); + STDMETHODIMP Initialize(GUID *lpGUID); + STDMETHODIMP RestoreDisplayMode(); + STDMETHODIMP SetCooperativeLevel(HWND hWnd,DWORD dwFlags); + STDMETHODIMP SetDisplayMode(DWORD dwWidth,DWORD dwHeight,DWORD dwBpp); + STDMETHODIMP WaitForVerticalBlank(DWORD dwFlags,HANDLE hEvent); +}; + + +// Class that aggregates an IDirectDrawSurface interface + +class CAggDrawSurface : public IDirectDrawSurface, public CUnknown +{ +protected: + + LPDIRECTDRAWSURFACE m_pDirectDrawSurface; + +public: + + DECLARE_IUNKNOWN + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv); + + // Constructor and destructor + + CAggDrawSurface(TCHAR *pName,LPUNKNOWN pUnk) : + CUnknown(pName,pUnk), + m_pDirectDrawSurface(NULL) { }; + + virtual ~CAggDrawSurface() { }; + + // Set the object we should be aggregating + void SetDirectDrawSurface(LPDIRECTDRAWSURFACE pDirectDrawSurface) { + m_pDirectDrawSurface = pDirectDrawSurface; + } + + // IDirectDrawSurface methods + + STDMETHODIMP AddAttachedSurface(LPDIRECTDRAWSURFACE lpDDSAttachedSurface); + STDMETHODIMP AddOverlayDirtyRect(LPRECT lpRect); + STDMETHODIMP Blt(LPRECT lpDestRect,LPDIRECTDRAWSURFACE lpDDSrcSurface,LPRECT lpSrcRect,DWORD dwFlags,LPDDBLTFX lpDDBltFx); + STDMETHODIMP BltBatch(LPDDBLTBATCH lpDDBltBatch,DWORD dwCount,DWORD dwFlags); + STDMETHODIMP BltFast(DWORD dwX,DWORD dwY,LPDIRECTDRAWSURFACE lpDDSrcSurface,LPRECT lpSrcRect,DWORD dwTrans); + STDMETHODIMP DeleteAttachedSurface(DWORD dwFlags,LPDIRECTDRAWSURFACE lpDDSAttachedSurface); + STDMETHODIMP EnumAttachedSurfaces(LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback); + STDMETHODIMP EnumOverlayZOrders(DWORD dwFlags,LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpfnCallback); + STDMETHODIMP Flip(LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride,DWORD dwFlags); + STDMETHODIMP GetAttachedSurface(LPDDSCAPS lpDDSCaps,LPDIRECTDRAWSURFACE *lplpDDAttachedSurface); + STDMETHODIMP GetBltStatus(DWORD dwFlags); + STDMETHODIMP GetCaps(LPDDSCAPS lpDDSCaps); + STDMETHODIMP GetClipper(LPDIRECTDRAWCLIPPER *lplpDDClipper); + STDMETHODIMP GetColorKey(DWORD dwFlags,LPDDCOLORKEY lpDDColorKey); + STDMETHODIMP GetDC(HDC *lphDC); + STDMETHODIMP GetFlipStatus(DWORD dwFlags); + STDMETHODIMP GetOverlayPosition(LPLONG lpdwX,LPLONG lpdwY); + STDMETHODIMP GetPalette(LPDIRECTDRAWPALETTE *lplpDDPalette); + STDMETHODIMP GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat); + STDMETHODIMP GetSurfaceDesc(LPDDSURFACEDESC lpDDSurfaceDesc); + STDMETHODIMP Initialize(LPDIRECTDRAW lpDD,LPDDSURFACEDESC lpDDSurfaceDesc); + STDMETHODIMP IsLost(); + STDMETHODIMP Lock(LPRECT lpDestRect,LPDDSURFACEDESC lpDDSurfaceDesc,DWORD dwFlags,HANDLE hEvent); + STDMETHODIMP ReleaseDC(HDC hDC); + STDMETHODIMP Restore(); + STDMETHODIMP SetClipper(LPDIRECTDRAWCLIPPER lpDDClipper); + STDMETHODIMP SetColorKey(DWORD dwFlags,LPDDCOLORKEY lpDDColorKey); + STDMETHODIMP SetOverlayPosition(LONG dwX,LONG dwY); + STDMETHODIMP SetPalette(LPDIRECTDRAWPALETTE lpDDPalette); + STDMETHODIMP Unlock(LPVOID lpSurfaceData); + STDMETHODIMP UpdateOverlay(LPRECT lpSrcRect,LPDIRECTDRAWSURFACE lpDDDestSurface,LPRECT lpDestRect,DWORD dwFlags,LPDDOVERLAYFX lpDDOverlayFX); + STDMETHODIMP UpdateOverlayDisplay(DWORD dwFlags); + STDMETHODIMP UpdateOverlayZOrder(DWORD dwFlags,LPDIRECTDRAWSURFACE lpDDSReference); +}; + + +// DirectShow must work on multiple platforms. In particular, it also runs on +// Windows NT 3.51 which does not have DirectDraw capabilities. The filters +// cannot therefore link statically to the DirectDraw library. To make their +// lives that little bit easier we provide this class that manages loading +// and unloading the library and creating the initial IDirectDraw interface + +typedef DWORD (WINAPI *PGETFILEVERSIONINFOSIZE)(LPTSTR,LPDWORD); +typedef BOOL (WINAPI *PGETFILEVERSIONINFO)(LPTSTR,DWORD,DWORD,LPVOID); +typedef BOOL (WINAPI *PVERQUERYVALUE)(LPVOID,LPTSTR,LPVOID,PUINT); + +class CLoadDirectDraw +{ + LPDIRECTDRAW m_pDirectDraw; // The DirectDraw driver instance + HINSTANCE m_hDirectDraw; // Handle to the loaded library + +public: + + CLoadDirectDraw(); + ~CLoadDirectDraw(); + + HRESULT LoadDirectDraw(LPSTR szDevice); + void ReleaseDirectDraw(); + HRESULT IsDirectDrawLoaded(); + LPDIRECTDRAW GetDirectDraw(); + BOOL IsDirectDrawVersion1(); +}; + +#endif // __VIDEOCTL__ + diff --git a/stepmania/src/BaseClasses/vtrans.cpp b/stepmania/src/BaseClasses/vtrans.cpp new file mode 100644 index 0000000000..3e2766de9d --- /dev/null +++ b/stepmania/src/BaseClasses/vtrans.cpp @@ -0,0 +1,468 @@ +//------------------------------------------------------------------------------ +// File: Vtrans.cpp +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include +#include +// #include // now in precomp file streams.h + +CVideoTransformFilter::CVideoTransformFilter + ( TCHAR *pName, LPUNKNOWN pUnk, REFCLSID clsid) + : CTransformFilter(pName, pUnk, clsid) + , m_itrLate(0) + , m_nKeyFramePeriod(0) // No QM until we see at least 2 key frames + , m_nFramesSinceKeyFrame(0) + , m_bSkipping(FALSE) + , m_tDecodeStart(0) + , m_itrAvgDecode(300000) // 30mSec - probably allows skipping + , m_bQualityChanged(FALSE) { +#ifdef PERF + RegisterPerfId(); +#endif // PERF +} + + +CVideoTransformFilter::~CVideoTransformFilter() { + // nothing to do +} + + +// Reset our quality management state + +HRESULT CVideoTransformFilter::StartStreaming() { + m_itrLate = 0; + m_nKeyFramePeriod = 0; // No QM until we see at least 2 key frames + m_nFramesSinceKeyFrame = 0; + m_bSkipping = FALSE; + m_tDecodeStart = 0; + m_itrAvgDecode = 300000; // 30mSec - probably allows skipping + m_bQualityChanged = FALSE; + m_bSampleSkipped = FALSE; + return NOERROR; +} + + +// Overriden to reset quality management information + +HRESULT CVideoTransformFilter::EndFlush() { { + // Synchronize + CAutoLock lck(&m_csReceive); + + // Reset our stats + // + // Note - we don't want to call derived classes here, + // we only want to reset our internal variables and this + // is a convenient way to do it + CVideoTransformFilter::StartStreaming(); + } + return CTransformFilter::EndFlush(); +} + + +HRESULT CVideoTransformFilter::AbortPlayback(HRESULT hr) { + NotifyEvent(EC_ERRORABORT, hr, 0); + m_pOutput->DeliverEndOfStream(); + return hr; +} + + +// Receive() +// +// Accept a sample from upstream, decide whether to process it +// or drop it. If we process it then get a buffer from the +// allocator of the downstream connection, transform it into the +// new buffer and deliver it to the downstream filter. +// If we decide not to process it then we do not get a buffer. + +// Remember that although this code will notice format changes coming into +// the input pin, it will NOT change its output format if that results +// in the filter needing to make a corresponding output format change. Your +// derived filter will have to take care of that. (eg. a palette change if +// the input and output is an 8 bit format). If the input sample is discarded +// and nothing is sent out for this Receive, please remember to put the format +// change on the first output sample that you actually do send. +// If your filter will produce the same output type even when the input type +// changes, then this base class code will do everything you need. + +HRESULT CVideoTransformFilter::Receive(IMediaSample *pSample) { + // If the next filter downstream is the video renderer, then it may + // be able to operate in DirectDraw mode which saves copying the data + // and gives higher performance. In that case the buffer which we + // get from GetDeliveryBuffer will be a DirectDraw buffer, and + // drawing into this buffer draws directly onto the display surface. + // This means that any waiting for the correct time to draw occurs + // during GetDeliveryBuffer, and that once the buffer is given to us + // the video renderer will count it in its statistics as a frame drawn. + // This means that any decision to drop the frame must be taken before + // calling GetDeliveryBuffer. + + ASSERT(CritCheckIn(&m_csReceive)); + AM_MEDIA_TYPE *pmtOut, *pmt; +#ifdef DEBUG + FOURCCMap fccOut; +#endif + HRESULT hr; + ASSERT(pSample); + IMediaSample * pOutSample; + + // If no output pin to deliver to then no point sending us data + ASSERT(m_pOutput != NULL) ; + + // The source filter may dynamically ask us to start transforming from a + // different media type than the one we're using now. If we don't, we'll + // draw garbage. (typically, this is a palette change in the movie, + // but could be something more sinister like the compression type changing, + // or even the video size changing) + +#define rcS1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcSource +#define rcT1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcTarget + + pSample->GetMediaType(&pmt); + if(pmt != NULL && pmt->pbFormat != NULL) { + + // spew some debug output + ASSERT(!IsEqualGUID(pmt->majortype, GUID_NULL)); +#ifdef DEBUG + fccOut.SetFOURCC(&pmt->subtype); + LONG lCompression = HEADER(pmt->pbFormat)->biCompression; + LONG lBitCount = HEADER(pmt->pbFormat)->biBitCount; + LONG lStride = (HEADER(pmt->pbFormat)->biWidth * lBitCount + 7) / 8; + lStride = (lStride + 3) & ~3; + DbgLog((LOG_TRACE,3,TEXT("*Changing input type on the fly to"))); + DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"), + fccOut.GetFOURCC(), lCompression, lBitCount)); + DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"), + HEADER(pmt->pbFormat)->biHeight, + rcT1.left, rcT1.top, rcT1.right, rcT1.bottom)); + DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"), + rcS1.left, rcS1.top, rcS1.right, rcS1.bottom, + lStride)); +#endif + + // now switch to using the new format. I am assuming that the + // derived filter will do the right thing when its media type is + // switched and streaming is restarted. + + StopStreaming(); + m_pInput->CurrentMediaType() = *pmt; + DeleteMediaType(pmt); + // if this fails, playback will stop, so signal an error + hr = StartStreaming(); + if(FAILED(hr)) { + return AbortPlayback(hr); + } + } + + // Now that we have noticed any format changes on the input sample, it's + // OK to discard it. + + if(ShouldSkipFrame(pSample)) { + MSR_NOTE(m_idSkip); + m_bSampleSkipped = TRUE; + return NOERROR; + } + + // Set up the output sample + hr = InitializeOutputSample(pSample, &pOutSample); + + if(FAILED(hr)) { + return hr; + } + + m_bSampleSkipped = FALSE; + + // The renderer may ask us to on-the-fly to start transforming to a + // different format. If we don't obey it, we'll draw garbage + +#define rcS ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcSource +#define rcT ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcTarget + + pOutSample->GetMediaType(&pmtOut); + if(pmtOut != NULL && pmtOut->pbFormat != NULL) { + + // spew some debug output + ASSERT(!IsEqualGUID(pmtOut->majortype, GUID_NULL)); +#ifdef DEBUG + fccOut.SetFOURCC(&pmtOut->subtype); + LONG lCompression = HEADER(pmtOut->pbFormat)->biCompression; + LONG lBitCount = HEADER(pmtOut->pbFormat)->biBitCount; + LONG lStride = (HEADER(pmtOut->pbFormat)->biWidth * lBitCount + 7) / 8; + lStride = (lStride + 3) & ~3; + DbgLog((LOG_TRACE,3,TEXT("*Changing output type on the fly to"))); + DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"), + fccOut.GetFOURCC(), lCompression, lBitCount)); + DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"), + HEADER(pmtOut->pbFormat)->biHeight, + rcT.left, rcT.top, rcT.right, rcT.bottom)); + DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"), + rcS.left, rcS.top, rcS.right, rcS.bottom, + lStride)); +#endif + + // now switch to using the new format. I am assuming that the + // derived filter will do the right thing when its media type is + // switched and streaming is restarted. + + StopStreaming(); + m_pOutput->CurrentMediaType() = *pmtOut; + DeleteMediaType(pmtOut); + hr = StartStreaming(); + + if(SUCCEEDED(hr)) { + // a new format, means a new empty buffer, so wait for a keyframe + // before passing anything on to the renderer. + // !!! a keyframe may never come, so give up after 30 frames + DbgLog((LOG_TRACE,3,TEXT("Output format change means we must wait for a keyframe"))); + m_nWaitForKey = 30; + + // if this fails, playback will stop, so signal an error + } + else { + + // Must release the sample before calling AbortPlayback + // because we might be holding the win16 lock or + // ddraw lock + pOutSample->Release(); + AbortPlayback(hr); + return hr; + } + } + + // After a discontinuity, we need to wait for the next key frame + if(pSample->IsDiscontinuity() == S_OK) { + DbgLog((LOG_TRACE,3,TEXT("Non-key discontinuity - wait for keyframe"))); + m_nWaitForKey = 30; + } + + // Start timing the transform (and log it if PERF is defined) + + if(SUCCEEDED(hr)) { + m_tDecodeStart = timeGetTime(); + 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); + m_tDecodeStart = timeGetTime()-m_tDecodeStart; + m_itrAvgDecode = m_tDecodeStart*(10000/16) + 15*(m_itrAvgDecode/16); + + // Maybe we're waiting for a keyframe still? + if(m_nWaitForKey) + m_nWaitForKey--; + if(m_nWaitForKey && pSample->IsSyncPoint() == S_OK) + m_nWaitForKey = FALSE; + + // if so, then we don't want to pass this on to the renderer + if(m_nWaitForKey && hr == NOERROR) { + DbgLog((LOG_TRACE,3,TEXT("still waiting for a keyframe"))); + hr = S_FALSE; + } + } + + if(FAILED(hr)) { + DbgLog((LOG_TRACE,1,TEXT("Error from video 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.) + // Try not to return S_FALSE to a direct draw buffer (it's wasteful) + // Try to take the decision earlier - before you get it. + + if(hr == NOERROR) { + hr = m_pOutput->Deliver(pOutSample); + } + else { + // S_FALSE returned from Transform is a PRIVATE agreement + // We should return NOERROR from Receive() in this case 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) { + + // We must Release() the sample before doing anything + // like calling the filter graph because having the + // sample means we may have the DirectDraw lock + // (== win16 lock on some versions) + pOutSample->Release(); + m_bSampleSkipped = TRUE; + if(!m_bQualityChanged) { + m_bQualityChanged = TRUE; + NotifyEvent(EC_QUALITY_CHANGE,0,0); + } + return NOERROR; + } + } + } + + // release the output buffer. If the connected pin still needs it, + // it will have addrefed it itself. + pOutSample->Release(); + ASSERT(CritCheckIn(&m_csReceive)); + + return hr; +} + + + +BOOL CVideoTransformFilter::ShouldSkipFrame( IMediaSample * pIn) { + REFERENCE_TIME trStart, trStopAt; + HRESULT hr = pIn->GetTime(&trStart, &trStopAt); + + // Don't skip frames with no timestamps + if(hr != S_OK) + return FALSE; + + int itrFrame = (int)(trStopAt - trStart); // frame duration + + if(S_OK==pIn->IsSyncPoint()) { + MSR_INTEGER(m_idFrameType, 1); + if(m_nKeyFramePeriod < m_nFramesSinceKeyFrame) { + // record the max + m_nKeyFramePeriod = m_nFramesSinceKeyFrame; + } + m_nFramesSinceKeyFrame = 0; + m_bSkipping = FALSE; + } + else { + MSR_INTEGER(m_idFrameType, 2); + if(m_nFramesSinceKeyFrame>m_nKeyFramePeriod + && m_nKeyFramePeriod>0) { + // We haven't seen the key frame yet, but we were clearly being + // overoptimistic about how frequent they are. + m_nKeyFramePeriod = m_nFramesSinceKeyFrame; + } + } + + + // Whatever we might otherwise decide, + // if we are taking only a small fraction of the required frame time to decode + // then any quality problems are actually coming from somewhere else. + // Could be a net problem at the source for instance. In this case there's + // no point in us skipping frames here. + if(m_itrAvgDecode*4>itrFrame) { + + // Don't skip unless we are at least a whole frame late. + // (We would skip B frames if more than 1/2 frame late, but they're safe). + if(m_itrLate > itrFrame) { + + // Don't skip unless the anticipated key frame would be no more than + // 1 frame early. If the renderer has not been waiting (we *guess* + // it hasn't because we're late) then it will allow frames to be + // played early by up to a frame. + + // Let T = Stream time from now to anticipated next key frame + // = (frame duration) * (KeyFramePeriod - FramesSinceKeyFrame) + // So we skip if T - Late < one frame i.e. + // (duration) * (freq - FramesSince) - Late < duration + // or (duration) * (freq - FramesSince - 1) < Late + + // We don't dare skip until we have seen some key frames and have + // some idea how often they occur and they are reasonably frequent. + if(m_nKeyFramePeriod>0) { + // It would be crazy - but we could have a stream with key frames + // a very long way apart - and if they are further than about + // 3.5 minutes apart then we could get arithmetic overflow in + // reference time units. Therefore we switch to mSec at this point + int it = (itrFrame/10000) + * (m_nKeyFramePeriod-m_nFramesSinceKeyFrame - 1); + MSR_INTEGER(m_idTimeTillKey, it); + + // For debug - might want to see the details - dump them as scratch pad +#ifdef VTRANSPERF + MSR_INTEGER(0, itrFrame); + MSR_INTEGER(0, m_nFramesSinceKeyFrame); + MSR_INTEGER(0, m_nKeyFramePeriod); +#endif + if(m_itrLate/10000 > it) { + m_bSkipping = TRUE; + // Now we are committed. Once we start skipping, we + // cannot stop until we hit a key frame. + } + else { +#ifdef VTRANSPERF + MSR_INTEGER(0, 777770); // not near enough to next key +#endif + } + } + else { +#ifdef VTRANSPERF + MSR_INTEGER(0, 777771); // Next key not predictable +#endif + } + } + else { +#ifdef VTRANSPERF + MSR_INTEGER(0, 777772); // Less than one frame late + MSR_INTEGER(0, m_itrLate); + MSR_INTEGER(0, itrFrame); +#endif + } + } + else { +#ifdef VTRANSPERF + MSR_INTEGER(0, 777773); // Decode time short - not not worth skipping + MSR_INTEGER(0, m_itrAvgDecode); + MSR_INTEGER(0, itrFrame); +#endif + } + + ++m_nFramesSinceKeyFrame; + + if(m_bSkipping) { + // We will count down the lateness as we skip each frame. + // We re-assess each frame. The key frame might not arrive when expected. + // We reset m_itrLate if we get a new Quality message, but actually that's + // not likely because we're not sending frames on to the Renderer. In + // fact if we DID get another one it would mean that there's a long + // pipe between us and the renderer and we might need an altogether + // better strategy to avoid hunting! + m_itrLate = m_itrLate - itrFrame; + } + + MSR_INTEGER(m_idLate, (int)m_itrLate/10000); // Note how late we think we are + if(m_bSkipping) { + if(!m_bQualityChanged) { + m_bQualityChanged = TRUE; + NotifyEvent(EC_QUALITY_CHANGE,0,0); + } + } + return m_bSkipping; +} + + +HRESULT CVideoTransformFilter::AlterQuality(Quality q) { + // to reduce the amount of 64 bit arithmetic, m_itrLate is an int. + // +, -, >, == etc are not too bad, but * and / are painful. + if(m_itrLate>300000000) { + // Avoid overflow and silliness - more than 30 secs late is already silly + m_itrLate = 300000000; + } + else { + m_itrLate = (int)q.Late; + } + // We ignore the other fields + + // We're actually not very good at handling this. In non-direct draw mode + // most of the time can be spent in the renderer which can skip any frame. + // In that case we'd rather the renderer handled things. + // Nevertheless we will keep an eye on it and if we really start getting + // a very long way behind then we will actually skip - but we'll still tell + // the renderer (or whoever is downstream) that they should handle quality. + + return E_FAIL; // Tell the renderer to do his thing. + +} + + + +// This will avoid several hundred useless warnings if compiled -W4 by MS VC++ v4 +#pragma warning(disable:4514) + + diff --git a/stepmania/src/BaseClasses/vtrans.h b/stepmania/src/BaseClasses/vtrans.h new file mode 100644 index 0000000000..5135266941 --- /dev/null +++ b/stepmania/src/BaseClasses/vtrans.h @@ -0,0 +1,143 @@ +//------------------------------------------------------------------------------ +// File: VTrans.h +// +// Desc: DirectShow base classes - defines a video transform class. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// This class is derived from CTransformFilter, but is specialised to handle +// the requirements of video quality control by frame dropping. +// This is a non-in-place transform, (i.e. it copies the data) such as a decoder. + +class CVideoTransformFilter : public CTransformFilter +{ + public: + + CVideoTransformFilter(TCHAR *, LPUNKNOWN, REFCLSID clsid); + ~CVideoTransformFilter(); + HRESULT EndFlush(); + + // ================================================================= + // ----- override these bits --------------------------------------- + // ================================================================= + // The following methods are in CTransformFilter which is inherited. + // They are mentioned here for completeness + // + // These MUST be supplied in a derived class + // + // NOTE: + // virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut); + // virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE; + // virtual HRESULT CheckTransform + // (const CMediaType* mtIn, const CMediaType* mtOut) PURE; + // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *); + // virtual HRESULT DecideBufferSize + // (IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop) PURE; + // virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE; + // + // These MAY also be overridden + // + // virtual HRESULT StopStreaming(); + // virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt); + // virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin); + // virtual HRESULT BreakConnect(PIN_DIRECTION dir); + // virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin); + // 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 + + // If you override this - ensure that you register all these ids + // as well as any of your own, + virtual void RegisterPerfId() { + m_idSkip = MSR_REGISTER(TEXT("Video Transform Skip frame")); + m_idFrameType = MSR_REGISTER(TEXT("Video transform frame type")); + m_idLate = MSR_REGISTER(TEXT("Video Transform Lateness")); + m_idTimeTillKey = MSR_REGISTER(TEXT("Video Transform Estd. time to next key")); + CTransformFilter::RegisterPerfId(); + } +#endif + + protected: + + // =========== QUALITY MANAGEMENT IMPLEMENTATION ======================== + // Frames are assumed to come in three types: + // Type 1: an AVI key frame or an MPEG I frame. + // This frame can be decoded with no history. + // Dropping this frame means that no further frame can be decoded + // until the next type 1 frame. + // Type 1 frames are sync points. + // Type 2: an AVI non-key frame or an MPEG P frame. + // This frame cannot be decoded unless the previous type 1 frame was + // decoded and all type 2 frames since have been decoded. + // Dropping this frame means that no further frame can be decoded + // until the next type 1 frame. + // Type 3: An MPEG B frame. + // This frame cannot be decoded unless the previous type 1 or 2 frame + // has been decoded AND the subsequent type 1 or 2 frame has also + // been decoded. (This requires decoding the frames out of sequence). + // Dropping this frame affects no other frames. This implementation + // does not allow for these. All non-sync-point frames are treated + // as being type 2. + // + // The spacing of frames of type 1 in a file is not guaranteed. There MUST + // be a type 1 frame at (well, near) the start of the file in order to start + // decoding at all. After that there could be one every half second or so, + // there could be one at the start of each scene (aka "cut", "shot") or + // there could be no more at all. + // If there is only a single type 1 frame then NO FRAMES CAN BE DROPPED + // without losing all the rest of the movie. There is no way to tell whether + // this is the case, so we find that we are in the gambling business. + // To try to improve the odds, we record the greatest interval between type 1s + // that we have seen and we bet on things being no worse than this in the + // future. + + // You can tell if it's a type 1 frame by calling IsSyncPoint(). + // there is no architected way to test for a type 3, so you should override + // the quality management here if you have B-frames. + + int m_nKeyFramePeriod; // the largest observed interval between type 1 frames + // 1 means every frame is type 1, 2 means every other. + + int m_nFramesSinceKeyFrame; // Used to count frames since the last type 1. + // becomes the new m_nKeyFramePeriod if greater. + + BOOL m_bSkipping; // we are skipping to the next type 1 frame + +#ifdef PERF + int m_idFrameType; // MSR id Frame type. 1=Key, 2="non-key" + int m_idSkip; // MSR id skipping + int m_idLate; // MSR id lateness + int m_idTimeTillKey; // MSR id for guessed time till next key frame. +#endif + + virtual HRESULT StartStreaming(); + + HRESULT AbortPlayback(HRESULT hr); // if something bad happens + + HRESULT Receive(IMediaSample *pSample); + + HRESULT AlterQuality(Quality q); + + BOOL ShouldSkipFrame(IMediaSample * pIn); + + int m_itrLate; // lateness from last Quality message + // (this overflows at 214 secs late). + int m_tDecodeStart; // timeGetTime when decode started. + int m_itrAvgDecode; // Average decode time in reference units. + + BOOL m_bNoSkip; // debug - no skipping. + + // We send an EC_QUALITY_CHANGE notification to the app if we have to degrade. + // We send one when we start degrading, not one for every frame, this means + // we track whether we've sent one yet. + BOOL m_bQualityChanged; + + // When non-zero, don't pass anything to renderer until next keyframe + // If there are few keys, give up and eventually draw something + int m_nWaitForKey; +}; diff --git a/stepmania/src/BaseClasses/winctrl.cpp b/stepmania/src/BaseClasses/winctrl.cpp new file mode 100644 index 0000000000..4771db98fd --- /dev/null +++ b/stepmania/src/BaseClasses/winctrl.cpp @@ -0,0 +1,1974 @@ +//------------------------------------------------------------------------------ +// File: WinCtrl.cpp +// +// Desc: DirectShow base classes - implements video control interface class. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include + +// The control interface methods require us to be connected + +#define CheckConnected(pin,code) \ +{ \ + if(pin == NULL) { \ + ASSERT(!TEXT("Pin not set")); \ + } \ + else if(pin->IsConnected() == FALSE) { \ + return (code); \ + } \ +} + +// This checks to see whether the window has a drain. An application can in +// most environments set the owner/parent of windows so that they appear in +// a compound document context (for example). In this case, the application +// would probably like to be told of any keyboard/mouse messages. Therefore +// we pass these messages on untranslated, returning TRUE if we're successful + +BOOL WINAPI PossiblyEatMessage(HWND hwndDrain, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if(hwndDrain != NULL && !InSendMessage()) { + switch(uMsg) { + case WM_CHAR: + case WM_DEADCHAR: + case WM_KEYDOWN: + case WM_KEYUP: + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEACTIVATE: + case WM_MOUSEMOVE: + // If we pass this on we don't get any mouse clicks + //case WM_NCHITTEST: + case WM_NCLBUTTONDBLCLK: + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONDBLCLK: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONUP: + case WM_NCMOUSEMOVE: + case WM_NCRBUTTONDBLCLK: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONUP: + case WM_RBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + + DbgLog((LOG_TRACE, 2, TEXT("Forwarding %x to drain"))); + PostMessage(hwndDrain, uMsg, wParam, lParam); + + return TRUE; + } + } + return FALSE; +} + + +// This class implements the IVideoWindow control functions (dual interface) +// we support a large number of properties and methods designed to allow the +// client (whether it be an automation controller or a C/C++ application) to +// set and get a number of window related properties such as it's position. +// We also support some methods that duplicate the properties but provide a +// more direct and efficient mechanism as many values may be changed in one + +CBaseControlWindow::CBaseControlWindow( + CBaseFilter *pFilter, // Owning filter + CCritSec *pInterfaceLock, // Locking object + TCHAR *pName, // Object description + LPUNKNOWN pUnk, // Normal COM ownership + HRESULT *phr) : // OLE return code + + CBaseVideoWindow(pName,pUnk), + m_pInterfaceLock(pInterfaceLock), + m_hwndOwner(NULL), + m_hwndDrain(NULL), + m_bAutoShow(TRUE), + m_pFilter(pFilter), + m_bCursorHidden(FALSE), + m_pPin(NULL) { + ASSERT(m_pFilter); + ASSERT(m_pInterfaceLock); + ASSERT(phr); + m_BorderColour = VIDEO_COLOUR; +} + + +// Set the title caption on the base window, we don't do any field checking +// as we really don't care what title they intend to have. We can always get +// it back again later with GetWindowText. The only other complication is to +// do the necessary string conversions between ANSI and OLE Unicode strings + +STDMETHODIMP CBaseControlWindow::put_Caption(BSTR strCaption) { + CheckPointer(strCaption,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); +#ifdef UNICODE + SetWindowText(m_hwnd, strCaption); +#else + CHAR Caption[CAPTION]; + + WideCharToMultiByte(CP_ACP,0,strCaption,-1,Caption,CAPTION,NULL,NULL); + SetWindowText(m_hwnd, Caption); +#endif + return NOERROR; +} + + +// Get the current base window title caption, once again we do no real field +// checking. We allocate a string for the window title to be filled in with +// which ensures the interface doesn't fiddle around with getting memory. A +// BSTR is a normal C string with the length at position (-1), we use the +// WriteBSTR helper function to create the caption to try and avoid OLE32 + +STDMETHODIMP CBaseControlWindow::get_Caption(BSTR *pstrCaption) { + CheckPointer(pstrCaption,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + WCHAR WideCaption[CAPTION]; + +#ifdef UNICODE + GetWindowText(m_hwnd,WideCaption,CAPTION); +#else + // Convert the ASCII caption to a UNICODE string + + TCHAR Caption[CAPTION]; + GetWindowText(m_hwnd,Caption,CAPTION); + MultiByteToWideChar(CP_ACP,0,Caption,-1,WideCaption,CAPTION); +#endif + return WriteBSTR(pstrCaption,WideCaption); +} + + +// Set the window style using GWL_EXSTYLE + +STDMETHODIMP CBaseControlWindow::put_WindowStyleEx(long WindowStyleEx) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Should we be taking off WS_EX_TOPMOST + + if(GetWindowLong(m_hwnd,GWL_EXSTYLE) & WS_EX_TOPMOST) { + if((WindowStyleEx & WS_EX_TOPMOST) == 0) { + SendMessage(m_hwnd,m_ShowStageTop,(WPARAM) FALSE,(LPARAM) 0); + } + } + + // Likewise should we be adding WS_EX_TOPMOST + + if(WindowStyleEx & WS_EX_TOPMOST) { + SendMessage(m_hwnd,m_ShowStageTop,(WPARAM) TRUE,(LPARAM) 0); + WindowStyleEx &= (~WS_EX_TOPMOST); + if(WindowStyleEx == 0) return NOERROR; + } + return DoSetWindowStyle(WindowStyleEx,GWL_EXSTYLE); +} + + +// Gets the current GWL_EXSTYLE base window style + +STDMETHODIMP CBaseControlWindow::get_WindowStyleEx(long *pWindowStyleEx) { + CheckPointer(pWindowStyleEx,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + return DoGetWindowStyle(pWindowStyleEx,GWL_EXSTYLE); +} + + +// Set the window style using GWL_STYLE + +STDMETHODIMP CBaseControlWindow::put_WindowStyle(long WindowStyle) { + // These styles cannot be changed dynamically + + if((WindowStyle & WS_DISABLED) || + (WindowStyle & WS_ICONIC) || + (WindowStyle & WS_MAXIMIZE) || + (WindowStyle & WS_MINIMIZE) || + (WindowStyle & WS_HSCROLL) || + (WindowStyle & WS_VSCROLL)) { + + return E_INVALIDARG; + } + + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + return DoSetWindowStyle(WindowStyle,GWL_STYLE); +} + + +// Get the current GWL_STYLE base window style + +STDMETHODIMP CBaseControlWindow::get_WindowStyle(long *pWindowStyle) { + CheckPointer(pWindowStyle,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + return DoGetWindowStyle(pWindowStyle,GWL_STYLE); +} + + +// Change the base window style or the extended styles depending on whether +// WindowLong is GWL_STYLE or GWL_EXSTYLE. We must call SetWindowPos to have +// the window displayed in it's new style after the change which is a little +// tricky if the window is not currently visible as we realise it offscreen. +// In most cases the client will call get_WindowStyle before they call this +// and then AND and OR in extra bit settings according to the requirements + +HRESULT CBaseControlWindow::DoSetWindowStyle(long Style,long WindowLong) { + RECT WindowRect; + + // Get the window's visibility before setting the style + BOOL bVisible = IsWindowVisible(m_hwnd); + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + // Set the new style flags for the window + SetWindowLong(m_hwnd,WindowLong,Style); + UINT WindowFlags = SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE; + WindowFlags |= SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE; + + // Show the window again in the current position + + if(bVisible == TRUE) { + + SetWindowPos(m_hwnd, // Base window handle + HWND_TOP, // Just a place holder + 0,0,0,0, // Leave size and position + WindowFlags); // Just draw it again + + return NOERROR; + } + + // Move the window offscreen so the user doesn't see the changes + + MoveWindow((HWND) m_hwnd, // Base window handle + GetSystemMetrics(SM_CXSCREEN), // Current desktop width + GetSystemMetrics(SM_CYSCREEN), // Likewise it's height + WIDTH(&WindowRect), // Use the same width + HEIGHT(&WindowRect), // Keep height same to + TRUE); // May as well repaint + + // Now show the previously hidden window + + SetWindowPos(m_hwnd, // Base window handle + HWND_TOP, // Just a place holder + 0,0,0,0, // Leave size and position + WindowFlags); // Just draw it again + + ShowWindow(m_hwnd,SW_HIDE); + + if(GetParent(m_hwnd)) { + + MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2); + } + + MoveWindow((HWND) m_hwnd, // Base window handle + WindowRect.left, // Existing x coordinate + WindowRect.top, // Existing y coordinate + WIDTH(&WindowRect), // Use the same width + HEIGHT(&WindowRect), // Keep height same to + TRUE); // May as well repaint + + return NOERROR; +} + + +// Get the current base window style (either GWL_STYLE or GWL_EXSTYLE) + +HRESULT CBaseControlWindow::DoGetWindowStyle(long *pStyle,long WindowLong) { + *pStyle = GetWindowLong(m_hwnd,WindowLong); + return NOERROR; +} + + +// Change the visibility of the base window, this takes the same parameters +// as the ShowWindow Win32 API does, so the client can have the window hidden +// or shown, minimised to an icon, or maximised to play in full screen mode +// We pass the request on to the base window to actually make the change + +STDMETHODIMP CBaseControlWindow::put_WindowState(long WindowState) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + DoShowWindow(WindowState); + return NOERROR; +} + + +// Get the current window state, this function returns a subset of the SW bit +// settings available in ShowWindow, if the window is visible then SW_SHOW is +// set, if it is hidden then the SW_HIDDEN is set, if it is either minimised +// or maximised then the SW_MINIMIZE or SW_MAXIMIZE is set respectively. The +// other SW bit settings are really set commands not readable output values + +STDMETHODIMP CBaseControlWindow::get_WindowState(long *pWindowState) { + CheckPointer(pWindowState,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + ASSERT(pWindowState); + *pWindowState = FALSE; + + // Is the window visible, a window is termed visible if it is somewhere on + // the current desktop even if it is completely obscured by other windows + // so the flag is a style for each window set with the WS_VISIBLE bit + + if(IsWindowVisible(m_hwnd) == TRUE) { + + // Is the base window iconic + if(IsIconic(m_hwnd) == TRUE) { + *pWindowState |= SW_MINIMIZE; + } + + // Has the window been maximised + else if(IsZoomed(m_hwnd) == TRUE) { + *pWindowState |= SW_MAXIMIZE; + } + + // Window is normal + else { + *pWindowState |= SW_SHOW; + } + + } + else { + *pWindowState |= SW_HIDE; + } + return NOERROR; +} + + +// This makes sure that any palette we realise in the base window (through a +// media type or through the overlay interface) is done in the background and +// is therefore mapped to existing device entries rather than taking it over +// as it will do when we this window gets the keyboard focus. An application +// uses this to make sure it doesn't have it's palette removed by the window + +STDMETHODIMP CBaseControlWindow::put_BackgroundPalette(long BackgroundPalette) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cWindowLock(&m_WindowLock); + + // Check this is a valid automation boolean type + + if(BackgroundPalette != OATRUE) { + if(BackgroundPalette != OAFALSE) { + return E_INVALIDARG; + } + } + + // Make sure the window realises any palette it has again + + m_bBackground = (BYTE) (BackgroundPalette == OATRUE ? TRUE : FALSE); + PostMessage(m_hwnd,m_RealizePalette,0,0); + PaintWindow(FALSE); + + return NOERROR; +} + + +// This returns the current background realisation setting + +STDMETHODIMP +CBaseControlWindow::get_BackgroundPalette(long *pBackgroundPalette) { + CheckPointer(pBackgroundPalette,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cWindowLock(&m_WindowLock); + + // Get the current background palette setting + + *pBackgroundPalette = (m_bBackground == TRUE ? OATRUE : OAFALSE); + return NOERROR; +} + + +// Change the visibility of the base window + +STDMETHODIMP CBaseControlWindow::put_Visible(long Visible) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Check this is a valid automation boolean type + + if(Visible != OATRUE) { + if(Visible != OAFALSE) { + return E_INVALIDARG; + } + } + + // Convert the boolean visibility into SW_SHOW and SW_HIDE + + INT Mode = (Visible == OATRUE ? SW_SHOWNORMAL : SW_HIDE); + DoShowWindow(Mode); + return NOERROR; +} + + +// Return OATRUE if the window is currently visible otherwise OAFALSE + +STDMETHODIMP CBaseControlWindow::get_Visible(long *pVisible) { + CheckPointer(pVisible,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // See if the base window has a WS_VISIBLE style - this will return TRUE + // even if the window is completely obscured by other desktop windows, we + // return FALSE if the window is not showing because of earlier calls + + BOOL Mode = IsWindowVisible(m_hwnd); + *pVisible = (Mode == TRUE ? OATRUE : OAFALSE); + return NOERROR; +} + + +// Change the left position of the base window. This keeps the window width +// and height properties the same so it effectively shunts the window left or +// right accordingly - there is the Width property to change that dimension + +STDMETHODIMP CBaseControlWindow::put_Left(long Left) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bSuccess; + RECT WindowRect; + + // Get the current window position in a RECT + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + if(GetParent(m_hwnd)) { + + MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2); + } + + // Adjust the coordinates ready for SetWindowPos, the window rectangle we + // get back from GetWindowRect is in left,top,right and bottom while the + // coordinates SetWindowPos wants are left,top,width and height values + + WindowRect.bottom = WindowRect.bottom - WindowRect.top; + WindowRect.right = WindowRect.right - WindowRect.left; + UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE; + + bSuccess = SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + Left, // New left position + WindowRect.top, // Leave top alone + WindowRect.right, // The WIDTH (not right) + WindowRect.bottom, // The HEIGHT (not bottom) + WindowFlags); // Show window options + + if(bSuccess == FALSE) { + return E_INVALIDARG; + } + return NOERROR; +} + + +// Return the current base window left position + +STDMETHODIMP CBaseControlWindow::get_Left(long *pLeft) { + CheckPointer(pLeft,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT WindowRect; + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + *pLeft = WindowRect.left; + return NOERROR; +} + + +// Change the current width of the base window. This property complements the +// left position property so we must keep the left edge constant and expand or +// contract to the right, the alternative would be to change the left edge so +// keeping the right edge constant but this is maybe a little more intuitive + +STDMETHODIMP CBaseControlWindow::put_Width(long Width) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bSuccess; + RECT WindowRect; + + // Adjust the coordinates ready for SetWindowPos, the window rectangle we + // get back from GetWindowRect is in left,top,right and bottom while the + // coordinates SetWindowPos wants are left,top,width and height values + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + if(GetParent(m_hwnd)) { + + MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2); + } + + WindowRect.bottom = WindowRect.bottom - WindowRect.top; + UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE; + + // This seems to have a bug in that calling SetWindowPos on a window with + // just the width changing causes it to ignore the width that you pass in + // and sets it to a mimimum value of 110 pixels wide (Windows NT 3.51) + + bSuccess = SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + WindowRect.left, // Leave left alone + WindowRect.top, // Leave top alone + Width, // New WIDTH dimension + WindowRect.bottom, // The HEIGHT (not bottom) + WindowFlags); // Show window options + + if(bSuccess == FALSE) { + return E_INVALIDARG; + } + return NOERROR; +} + + +// Return the current base window width + +STDMETHODIMP CBaseControlWindow::get_Width(long *pWidth) { + CheckPointer(pWidth,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT WindowRect; + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + *pWidth = WindowRect.right - WindowRect.left; + return NOERROR; +} + + +// This allows the client program to change the top position for the window in +// the same way that changing the left position does not affect the width of +// the image so changing the top position does not affect the window height + +STDMETHODIMP CBaseControlWindow::put_Top(long Top) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bSuccess; + RECT WindowRect; + + // Get the current window position in a RECT + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + if(GetParent(m_hwnd)) { + + MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2); + } + + // Adjust the coordinates ready for SetWindowPos, the window rectangle we + // get back from GetWindowRect is in left,top,right and bottom while the + // coordinates SetWindowPos wants are left,top,width and height values + + WindowRect.bottom = WindowRect.bottom - WindowRect.top; + WindowRect.right = WindowRect.right - WindowRect.left; + UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE; + + bSuccess = SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + WindowRect.left, // Leave left alone + Top, // New top position + WindowRect.right, // The WIDTH (not right) + WindowRect.bottom, // The HEIGHT (not bottom) + WindowFlags); // Show window flags + + if(bSuccess == FALSE) { + return E_INVALIDARG; + } + return NOERROR; +} + + +// Return the current base window top position + +STDMETHODIMP CBaseControlWindow::get_Top(long *pTop) { + CheckPointer(pTop,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT WindowRect; + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + *pTop = WindowRect.top; + return NOERROR; +} + + +// Change the height of the window, this complements the top property so when +// we change this we must keep the top position for the base window, as said +// before we could keep the bottom and grow upwards although this is perhaps +// a little more intuitive since we already have a top position property + +STDMETHODIMP CBaseControlWindow::put_Height(long Height) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bSuccess; + RECT WindowRect; + + // Adjust the coordinates ready for SetWindowPos, the window rectangle we + // get back from GetWindowRect is in left,top,right and bottom while the + // coordinates SetWindowPos wants are left,top,width and height values + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + if(GetParent(m_hwnd)) { + + MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2); + } + + WindowRect.right = WindowRect.right - WindowRect.left; + UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE; + + bSuccess = SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + WindowRect.left, // Leave left alone + WindowRect.top, // Leave top alone + WindowRect.right, // The WIDTH (not right) + Height, // New height dimension + WindowFlags); // Show window flags + + if(bSuccess == FALSE) { + return E_INVALIDARG; + } + return NOERROR; +} + + +// Return the current base window height + +STDMETHODIMP CBaseControlWindow::get_Height(long *pHeight) { + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT WindowRect; + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + *pHeight = WindowRect.bottom - WindowRect.top; + return NOERROR; +} + + +// This can be called to change the owning window. Setting the owner is done +// through this function, however to make the window a true child window the +// style must also be set to WS_CHILD. After resetting the owner to NULL an +// application should also set the style to WS_OVERLAPPED | WS_CLIPCHILDREN. + +// We cannot lock the object here because the SetParent causes an interthread +// SendMessage to the owner window. If they are in GetState we will sit here +// incomplete with the critical section locked therefore blocking out source +// filter threads from accessing us. Because the source thread can't enter us +// it can't get buffers or call EndOfStream so the GetState will not complete + +STDMETHODIMP CBaseControlWindow::put_Owner(OAHWND Owner) { + // Check we are connected otherwise reject the call + + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + m_hwndOwner = (HWND) Owner; + HWND hwndParent = m_hwndOwner; + + // Add or remove WS_CHILD as appropriate + + LONG Style = GetWindowLong(m_hwnd,GWL_STYLE); + if(Owner == NULL) { + Style &= (~WS_CHILD); + } + else { + Style |= (WS_CHILD); + } + SetWindowLong(m_hwnd,GWL_STYLE,Style); + + // Don't call this with the filter locked + + SetParent(m_hwnd,hwndParent); + + PaintWindow(TRUE); + NOTE1("Changed parent %lx",hwndParent); + + return NOERROR; +} + + +// This complements the put_Owner to get the current owning window property +// we always return NOERROR although the returned window handle may be NULL +// to indicate no owning window (the desktop window doesn't qualify as one) +// If an application sets the owner we call SetParent, however that returns +// NULL until the WS_CHILD bit is set on, so we store the owner internally + +STDMETHODIMP CBaseControlWindow::get_Owner(OAHWND *Owner) { + CheckPointer(Owner,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + *Owner = (OAHWND) m_hwndOwner; + return NOERROR; +} + + +// And renderer supporting IVideoWindow may have an HWND set who will get any +// keyboard and mouse messages we receive posted on to them. This is separate +// from setting an owning window. By separating the two, applications may get +// messages sent on even when they have set no owner (perhaps it's maximised) + +STDMETHODIMP CBaseControlWindow::put_MessageDrain(OAHWND Drain) { + // Check we are connected otherwise reject the call + + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + m_hwndDrain = (HWND) Drain; + return NOERROR; +} + + +// Return the current message drain + +STDMETHODIMP CBaseControlWindow::get_MessageDrain(OAHWND *Drain) { + CheckPointer(Drain,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + *Drain = (OAHWND) m_hwndDrain; + return NOERROR; +} + + +// This is called by the filter graph to inform us of a message we should know +// is being sent to our owning window. We have this because as a child window +// we do not get certain messages that are only sent to top level windows. We +// must see the palette changed/changing/query messages so that we know if we +// have the foreground palette or not. We pass the message on to our window +// using SendMessage - this will cause an interthread send message to occur + +STDMETHODIMP +CBaseControlWindow::NotifyOwnerMessage(OAHWND hwnd, // Window handle + long uMsg, // Message ID + LONG_PTR wParam, // Parameters + LONG_PTR lParam) // for message +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Only interested in these Windows messages + + switch(uMsg) { + + case WM_SYSCOLORCHANGE: + case WM_PALETTECHANGED: + case WM_PALETTEISCHANGING: + case WM_QUERYNEWPALETTE: + case WM_DEVMODECHANGE: + case WM_DISPLAYCHANGE: + case WM_ACTIVATEAPP: + + // If we do not have an owner then ignore + + if(m_hwndOwner == NULL) { + return NOERROR; + } + SendMessage(m_hwnd,uMsg,(WPARAM)wParam,(LPARAM)lParam); + break; + + // do NOT fwd WM_MOVE. the parameters are the location of the parent + // window, NOT what the renderer should be looking at. But we need + // to make sure the overlay is moved with the parent window, so we + // do this. + case WM_MOVE: + PostMessage(m_hwnd,WM_PAINT,0,0); + break; + } + return NOERROR; +} + + +// Allow an application to have us set the base window in the foreground. We +// have this because it is difficult for one thread to do do this to a window +// owned by another thread. We ask the base window class to do the real work + +STDMETHODIMP CBaseControlWindow::SetWindowForeground(long Focus) { + // Check this is a valid automation boolean type + + if(Focus != OATRUE) { + if(Focus != OAFALSE) { + return E_INVALIDARG; + } + } + + // We shouldn't lock as this sends a message + + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bFocus = (Focus == OATRUE ? TRUE : FALSE); + DoSetWindowForeground(bFocus); + + return NOERROR; +} + + +// This allows a client to set the complete window size and position in one +// atomic operation. The same affect can be had by changing each dimension +// in turn through their individual properties although some flashing will +// occur as each of them gets updated (they are better set at design time) + +STDMETHODIMP +CBaseControlWindow::SetWindowPosition(long Left,long Top,long Width,long Height) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bSuccess; + + // Set the new size and position + UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE; + + ASSERT(IsWindow(m_hwnd)); + bSuccess = SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + Left, // Left position + Top, // Top position + Width, // Window width + Height, // Window height + WindowFlags); // Show window flags + ASSERT(bSuccess); +#ifdef DEBUG + DbgLog((LOG_TRACE, 1, TEXT("SWP failed error %d"), GetLastError())); +#endif + if(bSuccess == FALSE) { + return E_INVALIDARG; + } + return NOERROR; +} + + +// This complements the SetWindowPosition to return the current window place +// in device coordinates. As before the same information can be retrived by +// calling the property get functions individually but this is atomic and is +// therefore more suitable to a live environment rather than design time + +STDMETHODIMP +CBaseControlWindow::GetWindowPosition(long *pLeft,long *pTop,long *pWidth,long *pHeight) { + // Should check the pointers are not NULL + + CheckPointer(pLeft,E_POINTER); + CheckPointer(pTop,E_POINTER); + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT WindowRect; + + // Get the current window coordinates + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + // Convert the RECT into left,top,width and height values + + *pLeft = WindowRect.left; + *pTop = WindowRect.top; + *pWidth = WindowRect.right - WindowRect.left; + *pHeight = WindowRect.bottom - WindowRect.top; + + return NOERROR; +} + + +// When a window is maximised or iconic calling GetWindowPosition will return +// the current window position (likewise for the properties). However if the +// restored size (ie the size we'll return to when normally shown) is needed +// then this should be used. When in a normal position (neither iconic nor +// maximised) then this returns the same coordinates as GetWindowPosition + +STDMETHODIMP +CBaseControlWindow::GetRestorePosition(long *pLeft,long *pTop,long *pWidth,long *pHeight) { + // Should check the pointers are not NULL + + CheckPointer(pLeft,E_POINTER); + CheckPointer(pTop,E_POINTER); + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Use GetWindowPlacement to find the restore position + + WINDOWPLACEMENT Place; + Place.length = sizeof(WINDOWPLACEMENT); + EXECUTE_ASSERT(GetWindowPlacement(m_hwnd,&Place)); + + RECT WorkArea; + + // We must take into account any task bar present + + if(SystemParametersInfo(SPI_GETWORKAREA,0,&WorkArea,FALSE) == TRUE) { + if(GetParent(m_hwnd) == NULL) { + Place.rcNormalPosition.top += WorkArea.top; + Place.rcNormalPosition.bottom += WorkArea.top; + Place.rcNormalPosition.left += WorkArea.left; + Place.rcNormalPosition.right += WorkArea.left; + } + } + + // Convert the RECT into left,top,width and height values + + *pLeft = Place.rcNormalPosition.left; + *pTop = Place.rcNormalPosition.top; + *pWidth = Place.rcNormalPosition.right - Place.rcNormalPosition.left; + *pHeight = Place.rcNormalPosition.bottom - Place.rcNormalPosition.top; + + return NOERROR; +} + + +// Return the current border colour, if we are playing something to a subset +// of the base window display there is an outside area exposed. The default +// action is to paint this colour in the Windows background colour (defined +// as value COLOR_WINDOW) We reset to this default when we're disconnected + +STDMETHODIMP CBaseControlWindow::get_BorderColor(long *Color) { + CheckPointer(Color,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + *Color = (long) m_BorderColour; + return NOERROR; +} + + +// This can be called to set the current border colour + +STDMETHODIMP CBaseControlWindow::put_BorderColor(long Color) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Have the window repainted with the new border colour + + m_BorderColour = (COLORREF) Color; + PaintWindow(TRUE); + return NOERROR; +} + + +// Delegate fullscreen handling to plug in distributor + +STDMETHODIMP CBaseControlWindow::get_FullScreenMode(long *FullScreenMode) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CheckPointer(FullScreenMode,E_POINTER); + return E_NOTIMPL; +} + + +// Delegate fullscreen handling to plug in distributor + +STDMETHODIMP CBaseControlWindow::put_FullScreenMode(long FullScreenMode) { + return E_NOTIMPL; +} + + +// This sets the auto show property, this property causes the base window to +// be displayed whenever we change state. This allows an application to have +// to do nothing to have the window appear but still allow them to change the +// default behaviour if for example they want to keep it hidden for longer + +STDMETHODIMP CBaseControlWindow::put_AutoShow(long AutoShow) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Check this is a valid automation boolean type + + if(AutoShow != OATRUE) { + if(AutoShow != OAFALSE) { + return E_INVALIDARG; + } + } + + m_bAutoShow = (AutoShow == OATRUE ? TRUE : FALSE); + return NOERROR; +} + + +// This can be called to get the current auto show flag. The flag is updated +// when we connect and disconnect and through this interface all of which are +// controlled and serialised by means of the main renderer critical section + +STDMETHODIMP CBaseControlWindow::get_AutoShow(long *AutoShow) { + CheckPointer(AutoShow,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + *AutoShow = (m_bAutoShow == TRUE ? OATRUE : OAFALSE); + return NOERROR; +} + + +// Return the minimum ideal image size for the current video. This may differ +// to the actual video dimensions because we may be using DirectDraw hardware +// that has specific stretching requirements. For example the Cirrus Logic +// cards have a minimum stretch factor depending on the overlay surface size + +STDMETHODIMP +CBaseControlWindow::GetMinIdealImageSize(long *pWidth,long *pHeight) { + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + FILTER_STATE State; + + // Must not be stopped for this to work correctly + + m_pFilter->GetState(0,&State); + if(State == State_Stopped) { + return VFW_E_WRONG_STATE; + } + + RECT DefaultRect = GetDefaultRect(); + *pWidth = WIDTH(&DefaultRect); + *pHeight = HEIGHT(&DefaultRect); + return NOERROR; +} + + +// Return the maximum ideal image size for the current video. This may differ +// to the actual video dimensions because we may be using DirectDraw hardware +// that has specific stretching requirements. For example the Cirrus Logic +// cards have a maximum stretch factor depending on the overlay surface size + +STDMETHODIMP +CBaseControlWindow::GetMaxIdealImageSize(long *pWidth,long *pHeight) { + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + FILTER_STATE State; + + // Must not be stopped for this to work correctly + + m_pFilter->GetState(0,&State); + if(State == State_Stopped) { + return VFW_E_WRONG_STATE; + } + + RECT DefaultRect = GetDefaultRect(); + *pWidth = WIDTH(&DefaultRect); + *pHeight = HEIGHT(&DefaultRect); + return NOERROR; +} + + +// Allow an application to hide the cursor on our window + +STDMETHODIMP +CBaseControlWindow::HideCursor(long HideCursor) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Check this is a valid automation boolean type + + if(HideCursor != OATRUE) { + if(HideCursor != OAFALSE) { + return E_INVALIDARG; + } + } + + m_bCursorHidden = (HideCursor == OATRUE ? TRUE : FALSE); + return NOERROR; +} + + +// Returns whether we have the cursor hidden or not + +STDMETHODIMP CBaseControlWindow::IsCursorHidden(long *CursorHidden) { + CheckPointer(CursorHidden,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + *CursorHidden = (m_bCursorHidden == TRUE ? OATRUE : OAFALSE); + return NOERROR; +} + + +// This class implements the IBasicVideo control functions (dual interface) +// we support a large number of properties and methods designed to allow the +// client (whether it be an automation controller or a C/C++ application) to +// set and get a number of video related properties such as the native video +// size. We support some methods that duplicate the properties but provide a +// more direct and efficient mechanism as many values may be changed in one + +CBaseControlVideo::CBaseControlVideo( + CBaseFilter *pFilter, // Owning filter + CCritSec *pInterfaceLock, // Locking object + TCHAR *pName, // Object description + LPUNKNOWN pUnk, // Normal COM ownership + HRESULT *phr) : // OLE return code + + CBaseBasicVideo(pName,pUnk), + m_pFilter(pFilter), + m_pInterfaceLock(pInterfaceLock), + m_pPin(NULL) { + ASSERT(m_pFilter); + ASSERT(m_pInterfaceLock); + ASSERT(phr); +} + +// Return an approximate average time per frame + +STDMETHODIMP CBaseControlVideo::get_AvgTimePerFrame(REFTIME *pAvgTimePerFrame) { + CheckPointer(pAvgTimePerFrame,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if(pVideoInfo == NULL) + return E_OUTOFMEMORY; + COARefTime AvgTime(pVideoInfo->AvgTimePerFrame); + *pAvgTimePerFrame = (REFTIME) AvgTime; + + return NOERROR; +} + + +// Return an approximate bit rate for the video + +STDMETHODIMP CBaseControlVideo::get_BitRate(long *pBitRate) { + CheckPointer(pBitRate,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if(pVideoInfo == NULL) + return E_OUTOFMEMORY; + *pBitRate = pVideoInfo->dwBitRate; + return NOERROR; +} + + +// Return an approximate bit error rate + +STDMETHODIMP CBaseControlVideo::get_BitErrorRate(long *pBitErrorRate) { + CheckPointer(pBitErrorRate,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if(pVideoInfo == NULL) + return E_OUTOFMEMORY; + *pBitErrorRate = pVideoInfo->dwBitErrorRate; + return NOERROR; +} + + +// This returns the current video width + +STDMETHODIMP CBaseControlVideo::get_VideoWidth(long *pVideoWidth) { + CheckPointer(pVideoWidth,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if(pVideoInfo == NULL) + return E_OUTOFMEMORY; + *pVideoWidth = pVideoInfo->bmiHeader.biWidth; + return NOERROR; +} + + +// This returns the current video height + +STDMETHODIMP CBaseControlVideo::get_VideoHeight(long *pVideoHeight) { + CheckPointer(pVideoHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if(pVideoInfo == NULL) + return E_OUTOFMEMORY; + *pVideoHeight = pVideoInfo->bmiHeader.biHeight; + return NOERROR; +} + + +// This returns the current palette the video is using as an array allocated +// by the user. To remain consistent we use PALETTEENTRY fields to return the +// colours in rather than RGBQUADs that multimedia decided to use. The memory +// is allocated by the user so we simple copy each in turn. We check that the +// number of entries requested and the start position offset are both valid +// If the number of entries evaluates to zero then we return an S_FALSE code + +STDMETHODIMP CBaseControlVideo::GetVideoPaletteEntries(long StartIndex, + long Entries, + long *pRetrieved, + long *pPalette) { + CheckPointer(pRetrieved,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + CMediaType MediaType; + + // Get the video format from the derived class + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if(pVideoInfo == NULL) + return E_OUTOFMEMORY; + BITMAPINFOHEADER *pHeader = HEADER(pVideoInfo); + + // Is the current format palettised + + if(PALETTISED(pVideoInfo) == FALSE) { + *pRetrieved = 0; + return VFW_E_NO_PALETTE_AVAILABLE; + } + + // Do they just want to know how many are available + + if(pPalette == NULL) { + *pRetrieved = pHeader->biClrUsed; + return NOERROR; + } + + // Make sure the start position is a valid offset + + if(StartIndex >= (LONG) pHeader->biClrUsed || StartIndex < 0) { + *pRetrieved = 0; + return E_INVALIDARG; + } + + // Correct the number we can retrieve + + LONG Available = (LONG) pHeader->biClrUsed - StartIndex; + *pRetrieved = max(0,min(Available,Entries)); + if(*pRetrieved == 0) { + return S_FALSE; + } + + // Copy the palette entries to the output buffer + + PALETTEENTRY *pEntries = (PALETTEENTRY *) pPalette; + RGBQUAD *pColours = COLORS(pVideoInfo) + StartIndex; + + for(LONG Count = 0;Count < *pRetrieved;Count++) { + pEntries[Count].peRed = pColours[Count].rgbRed; + pEntries[Count].peGreen = pColours[Count].rgbGreen; + pEntries[Count].peBlue = pColours[Count].rgbBlue; + pEntries[Count].peFlags = 0; + } + return NOERROR; +} + + +// This returns the current video dimensions as a method rather than a number +// of individual property get calls. For the same reasons as said before we +// cannot access the renderer media type directly as the window object thread +// may be updating it since dynamic format changes may change these values + +STDMETHODIMP CBaseControlVideo::GetVideoSize(long *pWidth,long *pHeight) { + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + // Get the video format from the derived class + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if(pVideoInfo == NULL) + return E_OUTOFMEMORY; + *pWidth = pVideoInfo->bmiHeader.biWidth; + *pHeight = pVideoInfo->bmiHeader.biHeight; + return NOERROR; +} + + +// Set the source video rectangle as left,top,right and bottom coordinates +// rather than left,top,width and height as per OLE automation interfaces +// Then pass the rectangle on to the window object to set the source + +STDMETHODIMP +CBaseControlVideo::SetSourcePosition(long Left,long Top,long Width,long Height) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + SourceRect.left = Left; + SourceRect.top = Top; + SourceRect.right = Left + Width; + SourceRect.bottom = Top + Height; + + // Check the source rectangle is valid + + HRESULT hr = CheckSourceRect(&SourceRect); + if(FAILED(hr)) { + return hr; + } + + // Now set the source rectangle + + hr = SetSourceRect(&SourceRect); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the source rectangle in left,top,width and height rather than the +// left,top,right and bottom values that RECT uses (and which the window +// object returns through GetSourceRect) which requires a little work + +STDMETHODIMP +CBaseControlVideo::GetSourcePosition(long *pLeft,long *pTop,long *pWidth,long *pHeight) { + // Should check the pointers are non NULL + + CheckPointer(pLeft,E_POINTER); + CheckPointer(pTop,E_POINTER); + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT SourceRect; + + CAutoLock cInterfaceLock(m_pInterfaceLock); + GetSourceRect(&SourceRect); + + *pLeft = SourceRect.left; + *pTop = SourceRect.top; + *pWidth = WIDTH(&SourceRect); + *pHeight = HEIGHT(&SourceRect); + + return NOERROR; +} + + +// Set the video destination as left,top,right and bottom coordinates rather +// than the left,top,width and height uses as per OLE automation interfaces +// Then pass the rectangle on to the window object to set the destination + +STDMETHODIMP +CBaseControlVideo::SetDestinationPosition(long Left,long Top,long Width,long Height) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + + DestinationRect.left = Left; + DestinationRect.top = Top; + DestinationRect.right = Left + Width; + DestinationRect.bottom = Top + Height; + + // Check the target rectangle is valid + + HRESULT hr = CheckTargetRect(&DestinationRect); + if(FAILED(hr)) { + return hr; + } + + // Now set the new target rectangle + + hr = SetTargetRect(&DestinationRect); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the destination rectangle in left,top,width and height rather than +// the left,top,right and bottom values that RECT uses (and which the window +// object returns through GetDestinationRect) which requires a little work + +STDMETHODIMP +CBaseControlVideo::GetDestinationPosition(long *pLeft,long *pTop,long *pWidth,long *pHeight) { + // Should check the pointers are not NULL + + CheckPointer(pLeft,E_POINTER); + CheckPointer(pTop,E_POINTER); + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT DestinationRect; + + CAutoLock cInterfaceLock(m_pInterfaceLock); + GetTargetRect(&DestinationRect); + + *pLeft = DestinationRect.left; + *pTop = DestinationRect.top; + *pWidth = WIDTH(&DestinationRect); + *pHeight = HEIGHT(&DestinationRect); + + return NOERROR; +} + + +// Set the source left position, the source rectangle we get back from the +// window object is a true rectangle in left,top,right and bottom positions +// so all we have to do is to update the left position and pass it back. We +// must keep the current width constant when we're updating this property + +STDMETHODIMP CBaseControlVideo::put_SourceLeft(long SourceLeft) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + GetSourceRect(&SourceRect); + SourceRect.right = SourceLeft + WIDTH(&SourceRect); + SourceRect.left = SourceLeft; + + // Check the source rectangle is valid + + HRESULT hr = CheckSourceRect(&SourceRect); + if(FAILED(hr)) { + return hr; + } + + // Now set the source rectangle + + hr = SetSourceRect(&SourceRect); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the current left source video position + +STDMETHODIMP CBaseControlVideo::get_SourceLeft(long *pSourceLeft) { + CheckPointer(pSourceLeft,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + + GetSourceRect(&SourceRect); + *pSourceLeft = SourceRect.left; + return NOERROR; +} + + +// Set the source width, we get the current source rectangle and then update +// the right position to be the left position (thereby keeping it constant) +// plus the new source width we are passed in (it expands to the right) + +STDMETHODIMP CBaseControlVideo::put_SourceWidth(long SourceWidth) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + GetSourceRect(&SourceRect); + SourceRect.right = SourceRect.left + SourceWidth; + + // Check the source rectangle is valid + + HRESULT hr = CheckSourceRect(&SourceRect); + if(FAILED(hr)) { + return hr; + } + + // Now set the source rectangle + + hr = SetSourceRect(&SourceRect); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the current source width + +STDMETHODIMP CBaseControlVideo::get_SourceWidth(long *pSourceWidth) { + CheckPointer(pSourceWidth,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + + GetSourceRect(&SourceRect); + *pSourceWidth = WIDTH(&SourceRect); + return NOERROR; +} + + +// Set the source top position - changing this property does not affect the +// current source height. So changing this shunts the source rectangle up and +// down appropriately. Changing the height complements this functionality by +// keeping the top position constant and simply changing the source height + +STDMETHODIMP CBaseControlVideo::put_SourceTop(long SourceTop) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + GetSourceRect(&SourceRect); + SourceRect.bottom = SourceTop + HEIGHT(&SourceRect); + SourceRect.top = SourceTop; + + // Check the source rectangle is valid + + HRESULT hr = CheckSourceRect(&SourceRect); + if(FAILED(hr)) { + return hr; + } + + // Now set the source rectangle + + hr = SetSourceRect(&SourceRect); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the current top position + +STDMETHODIMP CBaseControlVideo::get_SourceTop(long *pSourceTop) { + CheckPointer(pSourceTop,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + + GetSourceRect(&SourceRect); + *pSourceTop = SourceRect.top; + return NOERROR; +} + + +// Set the source height + +STDMETHODIMP CBaseControlVideo::put_SourceHeight(long SourceHeight) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + GetSourceRect(&SourceRect); + SourceRect.bottom = SourceRect.top + SourceHeight; + + // Check the source rectangle is valid + + HRESULT hr = CheckSourceRect(&SourceRect); + if(FAILED(hr)) { + return hr; + } + + // Now set the source rectangle + + hr = SetSourceRect(&SourceRect); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the current source height + +STDMETHODIMP CBaseControlVideo::get_SourceHeight(long *pSourceHeight) { + CheckPointer(pSourceHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + + GetSourceRect(&SourceRect); + *pSourceHeight = HEIGHT(&SourceRect); + return NOERROR; +} + + +// Set the target left position, the target rectangle we get back from the +// window object is a true rectangle in left,top,right and bottom positions +// so all we have to do is to update the left position and pass it back. We +// must keep the current width constant when we're updating this property + +STDMETHODIMP CBaseControlVideo::put_DestinationLeft(long DestinationLeft) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + GetTargetRect(&DestinationRect); + DestinationRect.right = DestinationLeft + WIDTH(&DestinationRect); + DestinationRect.left = DestinationLeft; + + // Check the target rectangle is valid + + HRESULT hr = CheckTargetRect(&DestinationRect); + if(FAILED(hr)) { + return hr; + } + + // Now set the new target rectangle + + hr = SetTargetRect(&DestinationRect); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the left position for the destination rectangle + +STDMETHODIMP CBaseControlVideo::get_DestinationLeft(long *pDestinationLeft) { + CheckPointer(pDestinationLeft,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + + GetTargetRect(&DestinationRect); + *pDestinationLeft = DestinationRect.left; + return NOERROR; +} + + +// Set the destination width + +STDMETHODIMP CBaseControlVideo::put_DestinationWidth(long DestinationWidth) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + GetTargetRect(&DestinationRect); + DestinationRect.right = DestinationRect.left + DestinationWidth; + + // Check the target rectangle is valid + + HRESULT hr = CheckTargetRect(&DestinationRect); + if(FAILED(hr)) { + return hr; + } + + // Now set the new target rectangle + + hr = SetTargetRect(&DestinationRect); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the width for the destination rectangle + +STDMETHODIMP CBaseControlVideo::get_DestinationWidth(long *pDestinationWidth) { + CheckPointer(pDestinationWidth,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + + GetTargetRect(&DestinationRect); + *pDestinationWidth = WIDTH(&DestinationRect); + return NOERROR; +} + + +// Set the target top position - changing this property does not affect the +// current target height. So changing this shunts the target rectangle up and +// down appropriately. Changing the height complements this functionality by +// keeping the top position constant and simply changing the target height + +STDMETHODIMP CBaseControlVideo::put_DestinationTop(long DestinationTop) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + GetTargetRect(&DestinationRect); + DestinationRect.bottom = DestinationTop + HEIGHT(&DestinationRect); + DestinationRect.top = DestinationTop; + + // Check the target rectangle is valid + + HRESULT hr = CheckTargetRect(&DestinationRect); + if(FAILED(hr)) { + return hr; + } + + // Now set the new target rectangle + + hr = SetTargetRect(&DestinationRect); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the top position for the destination rectangle + +STDMETHODIMP CBaseControlVideo::get_DestinationTop(long *pDestinationTop) { + CheckPointer(pDestinationTop,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + + GetTargetRect(&DestinationRect); + *pDestinationTop = DestinationRect.top; + return NOERROR; +} + + +// Set the destination height + +STDMETHODIMP CBaseControlVideo::put_DestinationHeight(long DestinationHeight) { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + GetTargetRect(&DestinationRect); + DestinationRect.bottom = DestinationRect.top + DestinationHeight; + + // Check the target rectangle is valid + + HRESULT hr = CheckTargetRect(&DestinationRect); + if(FAILED(hr)) { + return hr; + } + + // Now set the new target rectangle + + hr = SetTargetRect(&DestinationRect); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the height for the destination rectangle + +STDMETHODIMP CBaseControlVideo::get_DestinationHeight(long *pDestinationHeight) { + CheckPointer(pDestinationHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + + GetTargetRect(&DestinationRect); + *pDestinationHeight = HEIGHT(&DestinationRect); + return NOERROR; +} + + +// Reset the source rectangle to the full video dimensions + +STDMETHODIMP CBaseControlVideo::SetDefaultSourcePosition() { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + HRESULT hr = SetDefaultSourceRect(); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return S_OK if we're using the default source otherwise S_FALSE + +STDMETHODIMP CBaseControlVideo::IsUsingDefaultSource() { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + return IsDefaultSourceRect(); +} + + +// Reset the video renderer to use the entire playback area + +STDMETHODIMP CBaseControlVideo::SetDefaultDestinationPosition() { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + HRESULT hr = SetDefaultTargetRect(); + if(FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return S_OK if we're using the default target otherwise S_FALSE + +STDMETHODIMP CBaseControlVideo::IsUsingDefaultDestination() { + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + return IsDefaultTargetRect(); +} + + +// Return a copy of the current image in the video renderer + +STDMETHODIMP +CBaseControlVideo::GetCurrentImage(long *pBufferSize,long *pVideoImage) { + CheckPointer(pBufferSize,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + FILTER_STATE State; + + // Make sure we are in a paused state + + if(pVideoImage != NULL) { + m_pFilter->GetState(0,&State); + if(State != State_Paused) { + return VFW_E_NOT_PAUSED; + } + return GetStaticImage(pBufferSize,pVideoImage); + } + + // Just return the memory required + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if(pVideoInfo == NULL) + return E_OUTOFMEMORY; + RECT SourceRect; + GetSourceRect(&SourceRect); + return GetImageSize(pVideoInfo,pBufferSize,&SourceRect); +} + + +// An application has two ways of using GetCurrentImage, one is to pass a real +// buffer which should be filled with the current image. The other is to pass +// a NULL buffer pointer which is interpreted as asking us to return how much +// memory is required for the image. The constraints for when the latter can +// be called are much looser. To calculate the memory required we synthesize +// a VIDEOINFO that takes into account the source rectangle that's being used + +HRESULT CBaseControlVideo::GetImageSize(VIDEOINFOHEADER *pVideoInfo, + LONG *pBufferSize, + RECT *pSourceRect) { + NOTE("Entering GetImageSize"); + ASSERT(pSourceRect); + + // Check we have the correct input parameters + + if(pSourceRect == NULL || + pVideoInfo == NULL || + pBufferSize == NULL) { + + return E_UNEXPECTED; + } + + // Is the data format compatible + + if(pVideoInfo->bmiHeader.biCompression != BI_RGB) { + if(pVideoInfo->bmiHeader.biCompression != BI_BITFIELDS) { + return E_INVALIDARG; + } + } + + ASSERT(IsRectEmpty(pSourceRect) == FALSE); + + BITMAPINFOHEADER bih; + bih.biWidth = WIDTH(pSourceRect); + bih.biHeight = HEIGHT(pSourceRect); + bih.biBitCount = pVideoInfo->bmiHeader.biBitCount; + LONG Size = DIBSIZE(bih); + Size += GetBitmapFormatSize(HEADER(pVideoInfo)) - SIZE_PREHEADER; + *pBufferSize = Size; + + return NOERROR; +} + + +// Given an IMediaSample containing a linear buffer with an image and a type +// describing the bitmap make a rendering of the image into the output buffer +// This may be called by derived classes who render typical video images to +// handle the IBasicVideo GetCurrentImage method. The pVideoImage pointer may +// be NULL when passed to GetCurrentImage in which case GetImageSize will be +// called instead, which will just do the calculation of the memory required + +HRESULT CBaseControlVideo::CopyImage(IMediaSample *pMediaSample, + VIDEOINFOHEADER *pVideoInfo, + LONG *pBufferSize, + BYTE *pVideoImage, + RECT *pSourceRect) { + NOTE("Entering CopyImage"); + ASSERT(pSourceRect); + BYTE *pCurrentImage; + + // Check we have an image to copy + + if(pMediaSample == NULL || pSourceRect == NULL || + pVideoInfo == NULL || pVideoImage == NULL || + pBufferSize == NULL) { + + return E_UNEXPECTED; + } + + // Is the data format compatible + + if(pVideoInfo->bmiHeader.biCompression != BI_RGB) { + if(pVideoInfo->bmiHeader.biCompression != BI_BITFIELDS) { + return E_INVALIDARG; + } + } + + ASSERT(IsRectEmpty(pSourceRect) == FALSE); + + BITMAPINFOHEADER bih; + bih.biWidth = WIDTH(pSourceRect); + bih.biHeight = HEIGHT(pSourceRect); + bih.biBitCount = pVideoInfo->bmiHeader.biBitCount; + LONG Size = GetBitmapFormatSize(HEADER(pVideoInfo)) - SIZE_PREHEADER; + LONG Total = Size + DIBSIZE(bih); + + // Make sure we have a large enough buffer + + if(*pBufferSize < Total) { + return E_OUTOFMEMORY; + } + + // Copy the BITMAPINFO + + CopyMemory((PVOID)pVideoImage, (PVOID)&pVideoInfo->bmiHeader, Size); + ((BITMAPINFOHEADER *)pVideoImage)->biWidth = WIDTH(pSourceRect); + ((BITMAPINFOHEADER *)pVideoImage)->biHeight = HEIGHT(pSourceRect); + ((BITMAPINFOHEADER *)pVideoImage)->biSizeImage = DIBSIZE(bih); + BYTE *pImageData = pVideoImage + Size; + + // Get the pointer to it's image data + + HRESULT hr = pMediaSample->GetPointer(&pCurrentImage); + if(FAILED(hr)) { + return hr; + } + + // Now we are ready to start copying the source scan lines + + LONG ScanLine = (pVideoInfo->bmiHeader.biBitCount / 8) * WIDTH(pSourceRect); + LONG LinesToSkip = pVideoInfo->bmiHeader.biHeight; + LinesToSkip -= pSourceRect->top + HEIGHT(pSourceRect); + pCurrentImage += LinesToSkip * DIBWIDTHBYTES(pVideoInfo->bmiHeader); + pCurrentImage += pSourceRect->left * (pVideoInfo->bmiHeader.biBitCount / 8); + + // Even money on this GP faulting sometime... + + for(LONG Line = 0;Line < HEIGHT(pSourceRect);Line++) { + CopyMemory((PVOID)pImageData, (PVOID)pCurrentImage, ScanLine); + pImageData += DIBWIDTHBYTES(*(BITMAPINFOHEADER *)pVideoImage); + pCurrentImage += DIBWIDTHBYTES(pVideoInfo->bmiHeader); + } + return NOERROR; +} + + +// Called when we change media types either during connection or dynamically +// We inform the filter graph and therefore the application that the video +// size may have changed, we don't bother looking to see if it really has as +// we leave that to the application - the dimensions are the event parameters + +HRESULT CBaseControlVideo::OnVideoSizeChange() { + // Get the video format from the derived class + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if(pVideoInfo == NULL) + return E_OUTOFMEMORY; + WORD Width = (WORD) pVideoInfo->bmiHeader.biWidth; + WORD Height = (WORD) pVideoInfo->bmiHeader.biHeight; + + return m_pFilter->NotifyEvent(EC_VIDEO_SIZE_CHANGED, + MAKELPARAM(Width,Height), + MAKEWPARAM(0,0)); +} + + +// Set the video source rectangle. We must check the source rectangle against +// the actual video dimensions otherwise when we come to draw the pictures we +// get access violations as GDI tries to touch data outside of the image data +// Although we store the rectangle in left, top, right and bottom coordinates +// instead of left, top, width and height as OLE uses we do take into account +// that the rectangle is used up to, but not including, the right column and +// bottom row of pixels, see the Win32 documentation on RECT for more details + +HRESULT CBaseControlVideo::CheckSourceRect(RECT *pSourceRect) { + CheckPointer(pSourceRect,E_POINTER); + LONG Width,Height; + GetVideoSize(&Width,&Height); + + // Check the coordinates are greater than zero + // and that the rectangle is valid (leftleft >= pSourceRect->right) || + (pSourceRect->left < 0) || + (pSourceRect->top >= pSourceRect->bottom) || + (pSourceRect->top < 0)) { + + return E_INVALIDARG; + } + + // Check the coordinates are less than the extents + + if((pSourceRect->right > Width) || + (pSourceRect->bottom > Height)) { + + return E_INVALIDARG; + } + return NOERROR; +} + + +// Check the target rectangle has some valid coordinates, which amounts to +// little more than checking the destination rectangle isn't empty. Derived +// classes may call this when they have their SetTargetRect method called to +// check the rectangle validity, we do not update the rectangles passed in +// Although we store the rectangle in left, top, right and bottom coordinates +// instead of left, top, width and height as OLE uses we do take into account +// that the rectangle is used up to, but not including, the right column and +// bottom row of pixels, see the Win32 documentation on RECT for more details + +HRESULT CBaseControlVideo::CheckTargetRect(RECT *pTargetRect) { + // Check the pointer is valid + + if(pTargetRect == NULL) { + return E_POINTER; + } + + // These overflow the WIDTH and HEIGHT checks + + if(pTargetRect->left > pTargetRect->right || + pTargetRect->top > pTargetRect->bottom) { + return E_INVALIDARG; + } + + // Check the rectangle has valid coordinates + + if(WIDTH(pTargetRect) <= 0 || HEIGHT(pTargetRect) <= 0) { + return E_INVALIDARG; + } + + ASSERT(IsRectEmpty(pTargetRect) == FALSE); + return NOERROR; +} + + diff --git a/stepmania/src/BaseClasses/winctrl.h b/stepmania/src/BaseClasses/winctrl.h new file mode 100644 index 0000000000..cd90697869 --- /dev/null +++ b/stepmania/src/BaseClasses/winctrl.h @@ -0,0 +1,224 @@ +//------------------------------------------------------------------------------ +// File: WinCtrl.h +// +// Desc: DirectShow base classes - defines classes for video control +// interfaces. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __WINCTRL__ +#define __WINCTRL__ + +#define ABSOL(x) (x < 0 ? -x : x) +#define NEGAT(x) (x > 0 ? -x : x) + +// Helper +BOOL WINAPI PossiblyEatMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +class CBaseControlWindow : public CBaseVideoWindow, public CBaseWindow +{ +protected: + + CBaseFilter *m_pFilter; // Pointer to owning media filter + CBasePin *m_pPin; // Controls media types for connection + CCritSec *m_pInterfaceLock; // Externally defined critical section + COLORREF m_BorderColour; // Current window border colour + BOOL m_bAutoShow; // What happens when the state changes + HWND m_hwndOwner; // Owner window that we optionally have + HWND m_hwndDrain; // HWND to post any messages received + BOOL m_bCursorHidden; // Should we hide the window cursor + +public: + + // Internal methods for other objects to get information out + + HRESULT DoSetWindowStyle(long Style,long WindowLong); + HRESULT DoGetWindowStyle(long *pStyle,long WindowLong); + BOOL IsAutoShowEnabled() { return m_bAutoShow; }; + COLORREF GetBorderColour() { return m_BorderColour; }; + HWND GetOwnerWindow() { return m_hwndOwner; }; + BOOL IsCursorHidden() { return m_bCursorHidden; }; + + inline BOOL PossiblyEatMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + return ::PossiblyEatMessage(m_hwndDrain, uMsg, wParam, lParam); + } + + // Derived classes must call this to set the pin the filter is using + // We don't have the pin passed in to the constructor (as we do with + // the CBaseFilter object) because filters typically create the + // pins dynamically when requested in CBaseFilter::GetPin. This can + // not be called from our constructor because is is a virtual method + + void SetControlWindowPin(CBasePin *pPin) { + m_pPin = pPin; + } + +public: + + CBaseControlWindow(CBaseFilter *pFilter, // Owning media filter + CCritSec *pInterfaceLock, // Locking object + TCHAR *pName, // Object description + LPUNKNOWN pUnk, // Normal COM ownership + HRESULT *phr); // OLE return code + + // These are the properties we support + + STDMETHODIMP put_Caption(BSTR strCaption); + STDMETHODIMP get_Caption(BSTR *pstrCaption); + STDMETHODIMP put_AutoShow(long AutoShow); + STDMETHODIMP get_AutoShow(long *AutoShow); + STDMETHODIMP put_WindowStyle(long WindowStyle); + STDMETHODIMP get_WindowStyle(long *pWindowStyle); + STDMETHODIMP put_WindowStyleEx(long WindowStyleEx); + STDMETHODIMP get_WindowStyleEx(long *pWindowStyleEx); + STDMETHODIMP put_WindowState(long WindowState); + STDMETHODIMP get_WindowState(long *pWindowState); + STDMETHODIMP put_BackgroundPalette(long BackgroundPalette); + STDMETHODIMP get_BackgroundPalette(long *pBackgroundPalette); + STDMETHODIMP put_Visible(long Visible); + STDMETHODIMP get_Visible(long *pVisible); + STDMETHODIMP put_Left(long Left); + STDMETHODIMP get_Left(long *pLeft); + STDMETHODIMP put_Width(long Width); + STDMETHODIMP get_Width(long *pWidth); + STDMETHODIMP put_Top(long Top); + STDMETHODIMP get_Top(long *pTop); + STDMETHODIMP put_Height(long Height); + STDMETHODIMP get_Height(long *pHeight); + STDMETHODIMP put_Owner(OAHWND Owner); + STDMETHODIMP get_Owner(OAHWND *Owner); + STDMETHODIMP put_MessageDrain(OAHWND Drain); + STDMETHODIMP get_MessageDrain(OAHWND *Drain); + STDMETHODIMP get_BorderColor(long *Color); + STDMETHODIMP put_BorderColor(long Color); + STDMETHODIMP get_FullScreenMode(long *FullScreenMode); + STDMETHODIMP put_FullScreenMode(long FullScreenMode); + + // And these are the methods + + STDMETHODIMP SetWindowForeground(long Focus); + STDMETHODIMP NotifyOwnerMessage(OAHWND hwnd,long uMsg,LONG_PTR wParam,LONG_PTR lParam); + STDMETHODIMP GetMinIdealImageSize(long *pWidth,long *pHeight); + STDMETHODIMP GetMaxIdealImageSize(long *pWidth,long *pHeight); + STDMETHODIMP SetWindowPosition(long Left,long Top,long Width,long Height); + STDMETHODIMP GetWindowPosition(long *pLeft,long *pTop,long *pWidth,long *pHeight); + STDMETHODIMP GetRestorePosition(long *pLeft,long *pTop,long *pWidth,long *pHeight); + STDMETHODIMP HideCursor(long HideCursor); + STDMETHODIMP IsCursorHidden(long *CursorHidden); +}; + +// This class implements the IBasicVideo interface + +class CBaseControlVideo : public CBaseBasicVideo +{ +protected: + + CBaseFilter *m_pFilter; // Pointer to owning media filter + CBasePin *m_pPin; // Controls media types for connection + CCritSec *m_pInterfaceLock; // Externally defined critical section + +public: + + // Derived classes must provide these for the implementation + + virtual HRESULT IsDefaultTargetRect() PURE; + virtual HRESULT SetDefaultTargetRect() PURE; + virtual HRESULT SetTargetRect(RECT *pTargetRect) PURE; + virtual HRESULT GetTargetRect(RECT *pTargetRect) PURE; + virtual HRESULT IsDefaultSourceRect() PURE; + virtual HRESULT SetDefaultSourceRect() PURE; + virtual HRESULT SetSourceRect(RECT *pSourceRect) PURE; + virtual HRESULT GetSourceRect(RECT *pSourceRect) PURE; + virtual HRESULT GetStaticImage(long *pBufferSize,long *pDIBImage) PURE; + + // Derived classes must override this to return a VIDEOINFO representing + // the video format. We cannot call IPin ConnectionMediaType to get this + // format because various filters dynamically change the type when using + // DirectDraw such that the format shows the position of the logical + // bitmap in a frame buffer surface, so the size might be returned as + // 1024x768 pixels instead of 320x240 which is the real video dimensions + + virtual VIDEOINFOHEADER *GetVideoFormat() PURE; + + // Helper functions for creating memory renderings of a DIB image + + HRESULT GetImageSize(VIDEOINFOHEADER *pVideoInfo, + LONG *pBufferSize, + RECT *pSourceRect); + + HRESULT CopyImage(IMediaSample *pMediaSample, + VIDEOINFOHEADER *pVideoInfo, + LONG *pBufferSize, + BYTE *pVideoImage, + RECT *pSourceRect); + + // Override this if you want notifying when the rectangles change + virtual HRESULT OnUpdateRectangles() { return NOERROR; }; + virtual HRESULT OnVideoSizeChange(); + + // Derived classes must call this to set the pin the filter is using + // We don't have the pin passed in to the constructor (as we do with + // the CBaseFilter object) because filters typically create the + // pins dynamically when requested in CBaseFilter::GetPin. This can + // not be called from our constructor because is is a virtual method + + void SetControlVideoPin(CBasePin *pPin) { + m_pPin = pPin; + } + + // Helper methods for checking rectangles + virtual HRESULT CheckSourceRect(RECT *pSourceRect); + virtual HRESULT CheckTargetRect(RECT *pTargetRect); + +public: + + CBaseControlVideo(CBaseFilter *pFilter, // Owning media filter + CCritSec *pInterfaceLock, // Serialise interface + TCHAR *pName, // Object description + LPUNKNOWN pUnk, // Normal COM ownership + HRESULT *phr); // OLE return code + + // These are the properties we support + + STDMETHODIMP get_AvgTimePerFrame(REFTIME *pAvgTimePerFrame); + STDMETHODIMP get_BitRate(long *pBitRate); + STDMETHODIMP get_BitErrorRate(long *pBitErrorRate); + STDMETHODIMP get_VideoWidth(long *pVideoWidth); + STDMETHODIMP get_VideoHeight(long *pVideoHeight); + STDMETHODIMP put_SourceLeft(long SourceLeft); + STDMETHODIMP get_SourceLeft(long *pSourceLeft); + STDMETHODIMP put_SourceWidth(long SourceWidth); + STDMETHODIMP get_SourceWidth(long *pSourceWidth); + STDMETHODIMP put_SourceTop(long SourceTop); + STDMETHODIMP get_SourceTop(long *pSourceTop); + STDMETHODIMP put_SourceHeight(long SourceHeight); + STDMETHODIMP get_SourceHeight(long *pSourceHeight); + STDMETHODIMP put_DestinationLeft(long DestinationLeft); + STDMETHODIMP get_DestinationLeft(long *pDestinationLeft); + STDMETHODIMP put_DestinationWidth(long DestinationWidth); + STDMETHODIMP get_DestinationWidth(long *pDestinationWidth); + STDMETHODIMP put_DestinationTop(long DestinationTop); + STDMETHODIMP get_DestinationTop(long *pDestinationTop); + STDMETHODIMP put_DestinationHeight(long DestinationHeight); + STDMETHODIMP get_DestinationHeight(long *pDestinationHeight); + + // And these are the methods + + STDMETHODIMP GetVideoSize(long *pWidth,long *pHeight); + STDMETHODIMP SetSourcePosition(long Left,long Top,long Width,long Height); + STDMETHODIMP GetSourcePosition(long *pLeft,long *pTop,long *pWidth,long *pHeight); + STDMETHODIMP GetVideoPaletteEntries(long StartIndex,long Entries,long *pRetrieved,long *pPalette); + STDMETHODIMP SetDefaultSourcePosition(); + STDMETHODIMP IsUsingDefaultSource(); + STDMETHODIMP SetDestinationPosition(long Left,long Top,long Width,long Height); + STDMETHODIMP GetDestinationPosition(long *pLeft,long *pTop,long *pWidth,long *pHeight); + STDMETHODIMP SetDefaultDestinationPosition(); + STDMETHODIMP IsUsingDefaultDestination(); + STDMETHODIMP GetCurrentImage(long *pBufferSize,long *pVideoImage); +}; + +#endif // __WINCTRL__ + diff --git a/stepmania/src/BaseClasses/winutil.cpp b/stepmania/src/BaseClasses/winutil.cpp new file mode 100644 index 0000000000..fd48700d00 --- /dev/null +++ b/stepmania/src/BaseClasses/winutil.cpp @@ -0,0 +1,2694 @@ +//------------------------------------------------------------------------------ +// File: WinUtil.cpp +// +// Desc: DirectShow base classes - implements generic window handler class. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include +#include +#include + +static UINT MsgDestroy; + +// Constructor + +CBaseWindow::CBaseWindow(BOOL bDoGetDC, bool bDoPostToDestroy) : + m_hInstance(g_hInst), + m_hwnd(NULL), + m_hdc(NULL), + m_bActivated(FALSE), + m_pClassName(NULL), + m_ClassStyles(0), + m_WindowStyles(0), + m_WindowStylesEx(0), + m_ShowStageMessage(0), + m_ShowStageTop(0), + m_MemoryDC(NULL), + m_hPalette(NULL), + m_bBackground(FALSE), +#ifdef DEBUG + m_bRealizing(FALSE), +#endif + m_bNoRealize(FALSE), + m_bDoPostToDestroy(bDoPostToDestroy) +{ + m_bDoGetDC = bDoGetDC; +} + + +// Prepare a window by spinning off a worker thread to do the creation and +// also poll the message input queue. We leave this to be called by derived +// classes because they might want to override methods like MessageLoop and +// InitialiseWindow, if we do this during construction they'll ALWAYS call +// this base class methods. We make the worker thread create the window so +// it owns it rather than the filter graph thread which is constructing us + +HRESULT CBaseWindow::PrepareWindow() +{ + if (m_hwnd) return NOERROR; + ASSERT(m_hwnd == NULL); + ASSERT(m_hdc == NULL); + + // Get the derived object's window and class styles + + m_pClassName = GetClassWindowStyles(&m_ClassStyles, + &m_WindowStyles, + &m_WindowStylesEx); + if (m_pClassName == NULL) { + return E_FAIL; + } + + // Register our special private messages + m_ShowStageMessage = RegisterWindowMessage(SHOWSTAGE); + + // RegisterWindowMessage() returns 0 if an error occurs. + if (0 == m_ShowStageMessage) { + return AmGetLastErrorToHResult(); + } + + m_ShowStageTop = RegisterWindowMessage(SHOWSTAGETOP); + if (0 == m_ShowStageTop) { + return AmGetLastErrorToHResult(); + } + + m_RealizePalette = RegisterWindowMessage(REALIZEPALETTE); + if (0 == m_RealizePalette) { + return AmGetLastErrorToHResult(); + } + + MsgDestroy = RegisterWindowMessage(TEXT("AM_DESTROY")); + if (0 == MsgDestroy) { + return AmGetLastErrorToHResult(); + } + + return DoCreateWindow(); +} + + +// Destructor just a placeholder so that we know it becomes virtual +// Derived classes MUST call DoneWithWindow in their destructors so +// that no messages arrive after the derived class constructor ends + +#ifdef DEBUG +CBaseWindow::~CBaseWindow() +{ + ASSERT(m_hwnd == NULL); + ASSERT(m_hdc == NULL); +} +#endif + + +// We use the sync worker event to have the window destroyed. All we do is +// signal the event and wait on the window thread handle. Trying to send it +// messages causes too many problems, furthermore to be on the safe side we +// just wait on the thread handle while it returns WAIT_TIMEOUT or there is +// a sent message to process on this thread. If the constructor failed to +// create the thread in the first place then the loop will get terminated + +HRESULT CBaseWindow::DoneWithWindow() +{ + // + // Before doing anything, check that someone has not already killed the + // Video Renderer window. If it has been killed we need to tidy up + // a DC that the window was using. If we don't do this check + // the following GetWindowThreadProcessId test fails, but the SendMessage + // goes nowhere and we leak the DC. + // + if (!IsWindow(m_hwnd)) { + + // + // This is not a leak, the window manager automatically free's + // hdc's that were got via GetDC, which is the case here. + // We set it to NULL so that we don't get any asserts later. + // + m_hdc = NULL; + + // + // We need to free this DC though because USER32 does not know + // anything about it. + // + if (m_MemoryDC) + { + EXECUTE_ASSERT(DeleteDC(m_MemoryDC)); + m_MemoryDC = NULL; + } + + // Reset the window variables + m_hwnd = NULL; + return NOERROR; + } + + + if (GetWindowThreadProcessId(m_hwnd, NULL) != GetCurrentThreadId()) { + + if (m_bDoPostToDestroy) { + + CAMEvent m_evDone; + + // We must post a message to destroy the window + // That way we can't be in the middle of processing a + // message posted to our window when we do go away + // Sending a message gives less synchronization. + PostMessage(m_hwnd, MsgDestroy, (WPARAM)(HANDLE)m_evDone, 0); + WaitDispatchingMessages(m_evDone, INFINITE); + } else { + SendMessage(m_hwnd, MsgDestroy, 0, 0); + } + return NOERROR; + } + const HWND hwnd = m_hwnd; + if (hwnd == NULL) { + return NOERROR; + } + + InactivateWindow(); + NOTE("Inactivated"); + + // Reset the window styles before destruction + + SetWindowLong(hwnd,GWL_STYLE,m_WindowStyles); + ASSERT(GetParent(hwnd) == NULL); + NOTE1("Reset window styles %d",m_WindowStyles); + + // UnintialiseWindow sets m_hwnd to NULL so save a copy + UninitialiseWindow(); + DbgLog((LOG_TRACE, 2, TEXT("Destroying 0x%8.8X"), hwnd)); + if (!DestroyWindow(hwnd)) { + DbgLog((LOG_TRACE, 0, TEXT("DestroyWindow %8.8X failed code %d"), + hwnd, GetLastError())); + DbgBreak(""); + } + + // Reset our state so we can be prepared again + + m_pClassName = NULL; + m_ClassStyles = 0; + m_WindowStyles = 0; + m_WindowStylesEx = 0; + m_ShowStageMessage = 0; + m_ShowStageTop = 0; + + return NOERROR; +} + + +// Called at the end to put the window in an inactive state. The pending list +// will always have been cleared by this time so event if the worker thread +// gets has been signaled and gets in to render something it will find both +// the state has been changed and that there are no available sample images +// Since we wait on the window thread to complete we don't lock the object + +HRESULT CBaseWindow::InactivateWindow() +{ + // Has the window been activated + if (m_bActivated == FALSE) { + return S_FALSE; + } + + m_bActivated = FALSE; + ShowWindow(m_hwnd,SW_HIDE); + return NOERROR; +} + + +HRESULT CBaseWindow::CompleteConnect() +{ + m_bActivated = FALSE; + return NOERROR; +} + +// This displays a normal window. We ask the base window class for default +// sizes which unless overriden will return DEFWIDTH and DEFHEIGHT. We go +// through a couple of extra hoops to get the client area the right size +// as the object specifies which accounts for the AdjustWindowRectEx calls +// We also DWORD align the left and top coordinates of the window here to +// maximise the chance of being able to use DCI/DirectDraw primary surface + +HRESULT CBaseWindow::ActivateWindow() +{ + // Has the window been sized and positioned already + + if (m_bActivated == TRUE || GetParent(m_hwnd) != NULL) { + + SetWindowPos(m_hwnd, // Our window handle + HWND_TOP, // Put it at the top + 0, 0, 0, 0, // Leave in current position + SWP_NOMOVE | // Don't change it's place + SWP_NOSIZE); // Change Z-order only + + m_bActivated = TRUE; + return S_FALSE; + } + + // Calculate the desired client rectangle + + RECT WindowRect, ClientRect = GetDefaultRect(); + GetWindowRect(m_hwnd,&WindowRect); + AdjustWindowRectEx(&ClientRect,GetWindowLong(m_hwnd,GWL_STYLE), + FALSE,GetWindowLong(m_hwnd,GWL_EXSTYLE)); + + // Align left and top edges on DWORD boundaries + + UINT WindowFlags = (SWP_NOACTIVATE | SWP_FRAMECHANGED); + WindowRect.left -= (WindowRect.left & 3); + WindowRect.top -= (WindowRect.top & 3); + + SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + WindowRect.left, // Align left edge + WindowRect.top, // And also top place + WIDTH(&ClientRect), // Horizontal size + HEIGHT(&ClientRect), // Vertical size + WindowFlags); // Don't show window + + m_bActivated = TRUE; + return NOERROR; +} + + +// This can be used to DWORD align the window for maximum performance + +HRESULT CBaseWindow::PerformanceAlignWindow() +{ + RECT ClientRect,WindowRect; + GetWindowRect(m_hwnd,&WindowRect); + ASSERT(m_bActivated == TRUE); + + // Don't do this if we're owned + + if (GetParent(m_hwnd)) { + return NOERROR; + } + + // Align left and top edges on DWORD boundaries + + GetClientRect(m_hwnd, &ClientRect); + MapWindowPoints(m_hwnd, HWND_DESKTOP, (LPPOINT) &ClientRect, 2); + WindowRect.left -= (ClientRect.left & 3); + WindowRect.top -= (ClientRect.top & 3); + UINT WindowFlags = (SWP_NOACTIVATE | SWP_NOSIZE); + + SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + WindowRect.left, // Align left edge + WindowRect.top, // And also top place + (int) 0,(int) 0, // Ignore these sizes + WindowFlags); // Don't show window + + return NOERROR; +} + + +// Install a palette into the base window - we may be called by a different +// thread to the one that owns the window. We have to be careful how we do +// the palette realisation as we could be a different thread to the window +// which would cause an inter thread send message. Therefore we realise the +// palette by sending it a special message but without the window locked + +HRESULT CBaseWindow::SetPalette(HPALETTE hPalette) +{ + // We must own the window lock during the change + { + CAutoLock cWindowLock(&m_WindowLock); + CAutoLock cPaletteLock(&m_PaletteLock); + ASSERT(hPalette); + m_hPalette = hPalette; + } + return SetPalette(); +} + + +HRESULT CBaseWindow::SetPalette() +{ + if (!m_bNoRealize) { + SendMessage(m_hwnd, m_RealizePalette, 0, 0); + return S_OK; + } else { + // Just select the palette + ASSERT(m_hdc); + ASSERT(m_MemoryDC); + + CAutoLock cPaletteLock(&m_PaletteLock); + SelectPalette(m_hdc,m_hPalette,m_bBackground); + SelectPalette(m_MemoryDC,m_hPalette,m_bBackground); + + return S_OK; + } +} + + +void CBaseWindow::UnsetPalette() +{ + CAutoLock cWindowLock(&m_WindowLock); + CAutoLock cPaletteLock(&m_PaletteLock); + + // Get a standard VGA colour palette + + HPALETTE hPalette = (HPALETTE) GetStockObject(DEFAULT_PALETTE); + ASSERT(hPalette); + + SelectPalette(GetWindowHDC(), hPalette, TRUE); + SelectPalette(GetMemoryHDC(), hPalette, TRUE); + + m_hPalette = NULL; +} + + +void CBaseWindow::LockPaletteLock() +{ + m_PaletteLock.Lock(); +} + + +void CBaseWindow::UnlockPaletteLock() +{ + m_PaletteLock.Unlock(); +} + + +// Realise our palettes in the window and device contexts + +HRESULT CBaseWindow::DoRealisePalette(BOOL bForceBackground) +{ + { + CAutoLock cPaletteLock(&m_PaletteLock); + + if (m_hPalette == NULL) { + return NOERROR; + } + + // Realize the palette on the window thread + ASSERT(m_hdc); + ASSERT(m_MemoryDC); + + SelectPalette(m_hdc,m_hPalette,m_bBackground || bForceBackground); + SelectPalette(m_MemoryDC,m_hPalette,m_bBackground); + } + + // If we grab a critical section here we can deadlock + // with the window thread because one of the side effects + // of RealizePalette is to send a WM_PALETTECHANGED message + // to every window in the system. In our handling + // of WM_PALETTECHANGED we used to grab this CS too. + // The really bad case is when our renderer calls DoRealisePalette() + // while we're in the middle of processing a palette change + // for another window. + // So don't hold the critical section while actually realising + // the palette. In any case USER is meant to manage palette + // handling - we shouldn't have to serialize everything as well + ASSERT(CritCheckOut(&m_WindowLock)); + ASSERT(CritCheckOut(&m_PaletteLock)); + + EXECUTE_ASSERT(RealizePalette(m_hdc) != GDI_ERROR); + EXECUTE_ASSERT(RealizePalette(m_MemoryDC) != GDI_ERROR); + + return (GdiFlush() == FALSE ? S_FALSE : S_OK); +} + + +// This is the global window procedure + +LRESULT CALLBACK WndProc(HWND hwnd, // Window handle + UINT uMsg, // Message ID + WPARAM wParam, // First parameter + LPARAM lParam) // Other parameter +{ + + // Get the window long that holds our window object pointer + // If it is NULL then we are initialising the window in which + // case the object pointer has been passed in the window creation + // structure. IF we get any messages before WM_NCCREATE we will + // pass them to DefWindowProc. + + CBaseWindow *pBaseWindow = (CBaseWindow *)GetWindowLongPtr(hwnd,0); + if (pBaseWindow == NULL) { + + // Get the structure pointer from the create struct. + // We can only do this for WM_NCCREATE which should be one of + // the first messages we receive. Anything before this will + // have to be passed to DefWindowProc (i.e. WM_GETMINMAXINFO) + + // If the message is WM_NCCREATE we set our pBaseWindow pointer + // and will then place it in the window structure + + // turn off WS_EX_LAYOUTRTL style for quartz windows + if (uMsg == WM_NCCREATE) { + SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) & ~0x400000); + } + + if ((uMsg != WM_NCCREATE) + || (NULL == (pBaseWindow = *(CBaseWindow**) ((LPCREATESTRUCT)lParam)->lpCreateParams))) + { + return(DefWindowProc(hwnd, uMsg, wParam, lParam)); + } + + // Set the window LONG to be the object who created us +#ifdef DEBUG + SetLastError(0); // because of the way SetWindowLong works +#endif + LONG_PTR rc = SetWindowLongPtr(hwnd, (DWORD) 0, (LONG_PTR) pBaseWindow); +#ifdef DEBUG + if (0 == rc) { + // SetWindowLong MIGHT have failed. (Read the docs which admit + // that it is awkward to work out if you have had an error.) + LONG lasterror = GetLastError(); + ASSERT(0 == lasterror); + // If this is not the case we have not set the pBaseWindow pointer + // into the window structure and we will blow up. + } +#endif + + } + // See if this is the packet of death + if (uMsg == MsgDestroy && uMsg != 0) { + pBaseWindow->DoneWithWindow(); + if (pBaseWindow->m_bDoPostToDestroy) { + EXECUTE_ASSERT(SetEvent((HANDLE)wParam)); + } + return 0; + } + return pBaseWindow->OnReceiveMessage(hwnd,uMsg,wParam,lParam); +} + + +// When the window size changes we adjust our member variables that +// contain the dimensions of the client rectangle for our window so +// that we come to render an image we will know whether to stretch + +BOOL CBaseWindow::OnSize(LONG Width, LONG Height) +{ + m_Width = Width; + m_Height = Height; + return TRUE; +} + + +// This function handles the WM_CLOSE message + +BOOL CBaseWindow::OnClose() +{ + ShowWindow(m_hwnd,SW_HIDE); + return TRUE; +} + + +// This is called by the worker window thread when it receives a terminate +// message from the window object destructor to delete all the resources we +// allocated during initialisation. By the time the worker thread exits all +// processing will have been completed as the source filter disconnection +// flushes the image pending sample, therefore the GdiFlush should succeed + +HRESULT CBaseWindow::UninitialiseWindow() +{ + // Have we already cleaned up + + if (m_hwnd == NULL) { + ASSERT(m_hdc == NULL); + ASSERT(m_MemoryDC == NULL); + return NOERROR; + } + + // Release the window resources + + EXECUTE_ASSERT(GdiFlush()); + + if (m_hdc) + { + EXECUTE_ASSERT(ReleaseDC(m_hwnd,m_hdc)); + m_hdc = NULL; + } + + if (m_MemoryDC) + { + EXECUTE_ASSERT(DeleteDC(m_MemoryDC)); + m_MemoryDC = NULL; + } + + // Reset the window variables + m_hwnd = NULL; + + return NOERROR; +} + + +// This is called by the worker window thread after it has created the main +// window and it wants to initialise the rest of the owner objects window +// variables such as the device contexts. We execute this function with the +// critical section still locked. Nothing in this function must generate any +// SendMessage calls to the window because this is executing on the window +// thread so the message will never be processed and we will deadlock + +#pragma warning(disable:4706) // C4706: assignment within conditional expression + +HRESULT CBaseWindow::InitialiseWindow(HWND hwnd) +{ + // Initialise the window variables + + ASSERT(IsWindow(hwnd)); + m_hwnd = hwnd; + + if (m_bDoGetDC) + { + EXECUTE_ASSERT(m_hdc = GetDC(hwnd)); + EXECUTE_ASSERT(m_MemoryDC = CreateCompatibleDC(m_hdc)); + + EXECUTE_ASSERT(SetStretchBltMode(m_hdc,COLORONCOLOR)); + EXECUTE_ASSERT(SetStretchBltMode(m_MemoryDC,COLORONCOLOR)); + } + + return NOERROR; +} + +#pragma warning(default:4706) // C4706: assignment within conditional expression + + +HRESULT CBaseWindow::DoCreateWindow() +{ + WNDCLASS wndclass; // Used to register classes + BOOL bRegistered; // Is this class registered + HWND hwnd; // Handle to our window + + bRegistered = GetClassInfo(m_hInstance, // Module instance + m_pClassName, // Window class + &wndclass); // Info structure + + // if the window is to be used for drawing puposes and we are getting a DC + // for the entire lifetime of the window then changes the class style to do + // say so. If we don't set this flag then the DC comes from the cache and is + // really bad. + if (m_bDoGetDC) + { + m_ClassStyles |= CS_OWNDC; + } + + if (bRegistered == FALSE) { + + // Register the renderer window class + + wndclass.lpszClassName = m_pClassName; + wndclass.style = m_ClassStyles; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = sizeof(CBaseWindow *); + wndclass.hInstance = m_hInstance; + wndclass.hIcon = NULL; + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground = (HBRUSH) NULL; + wndclass.lpszMenuName = NULL; + + RegisterClass(&wndclass); + } + + // Create the frame window. Pass the pBaseWindow information in the + // CreateStruct which allows our message handling loop to get hold of + // the pBaseWindow pointer. + + CBaseWindow *pBaseWindow = this; // The owner window object + hwnd = CreateWindowEx(m_WindowStylesEx, // Extended styles + m_pClassName, // Registered name + TEXT("ActiveMovie Window"), // Window title + m_WindowStyles, // Window styles + CW_USEDEFAULT, // Start x position + CW_USEDEFAULT, // Start y position + DEFWIDTH, // Window width + DEFHEIGHT, // Window height + NULL, // Parent handle + NULL, // Menu handle + m_hInstance, // Instance handle + &pBaseWindow); // Creation data + + // If we failed signal an error to the object constructor (based on the + // last Win32 error on this thread) then signal the constructor thread + // to continue, release the mutex to let others have a go and exit + + if (hwnd == NULL) { + DWORD Error = GetLastError(); + return AmHresultFromWin32(Error); + } + + // Check the window LONG is the object who created us + ASSERT(GetWindowLongPtr(hwnd, 0) == (LONG_PTR)this); + + // Initialise the window and then signal the constructor so that it can + // continue and then finally unlock the object's critical section. The + // window class is left registered even after we terminate the thread + // as we don't know when the last window has been closed. So we allow + // the operating system to free the class resources as appropriate + + InitialiseWindow(hwnd); + + DbgLog((LOG_TRACE, 2, TEXT("Created window class (%s) HWND(%8.8X)"), + m_pClassName, hwnd)); + + return S_OK; +} + + +// The base class provides some default handling and calls DefWindowProc + +LRESULT CBaseWindow::OnReceiveMessage(HWND hwnd, // Window handle + UINT uMsg, // Message ID + WPARAM wParam, // First parameter + LPARAM lParam) // Other parameter +{ + ASSERT(IsWindow(hwnd)); + + if (PossiblyEatMessage(uMsg, wParam, lParam)) + return 0; + + // This is sent by the IVideoWindow SetWindowForeground method. If the + // window is invisible we will show it and make it topmost without the + // foreground focus. If the window is visible it will also be made the + // topmost window without the foreground focus. If wParam is TRUE then + // for both cases the window will be forced into the foreground focus + + if (uMsg == m_ShowStageMessage) { + + BOOL bVisible = IsWindowVisible(hwnd); + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + (bVisible ? SWP_NOACTIVATE : 0)); + + // Should we bring the window to the foreground + if (wParam == TRUE) { + SetForegroundWindow(hwnd); + } + return (LRESULT) 1; + } + + // When we go fullscreen we have to add the WS_EX_TOPMOST style to the + // video window so that it comes out above any task bar (this is more + // relevant to WindowsNT than Windows95). However the SetWindowPos call + // must be on the same thread as that which created the window. The + // wParam parameter can be TRUE or FALSE to set and reset the topmost + + if (uMsg == m_ShowStageTop) { + HWND HwndTop = (wParam == TRUE ? HWND_TOPMOST : HWND_NOTOPMOST); + BOOL bVisible = IsWindowVisible(hwnd); + SetWindowPos(hwnd, HwndTop, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | + (wParam == TRUE ? SWP_SHOWWINDOW : 0) | + (bVisible ? SWP_NOACTIVATE : 0)); + return (LRESULT) 1; + } + + // New palette stuff + if (uMsg == m_RealizePalette) { + ASSERT(m_hwnd == hwnd); + return OnPaletteChange(m_hwnd,WM_QUERYNEWPALETTE); + } + + switch (uMsg) { + + // Repaint the window if the system colours change + + case WM_SYSCOLORCHANGE: + + InvalidateRect(hwnd,NULL,FALSE); + return (LRESULT) 1; + + // Somebody has changed the palette + case WM_PALETTECHANGED: + + OnPaletteChange((HWND)wParam,uMsg); + return (LRESULT) 0; + + // We are about to receive the keyboard focus so we ask GDI to realise + // our logical palette again and hopefully it will be fully installed + // without any mapping having to be done during any picture rendering + + case WM_QUERYNEWPALETTE: + ASSERT(m_hwnd == hwnd); + return OnPaletteChange(m_hwnd,uMsg); + + // do NOT fwd WM_MOVE. the parameters are the location of the parent + // window, NOT what the renderer should be looking at. But we need + // to make sure the overlay is moved with the parent window, so we + // do this. + case WM_MOVE: + if (IsWindowVisible(m_hwnd)) { + PostMessage(m_hwnd,WM_PAINT,0,0); + } + break; + + // Store the width and height as useful base class members + + case WM_SIZE: + + OnSize(LOWORD(lParam), HIWORD(lParam)); + return (LRESULT) 0; + + // Intercept the WM_CLOSE messages to hide the window + + case WM_CLOSE: + + OnClose(); + return (LRESULT) 0; + } + return DefWindowProc(hwnd,uMsg,wParam,lParam); +} + + +// This handles the Windows palette change messages - if we do realise our +// palette then we return TRUE otherwise we return FALSE. If our window is +// foreground application then we should get first choice of colours in the +// system palette entries. We get best performance when our logical palette +// includes the standard VGA colours (at the beginning and end) otherwise +// GDI may have to map from our palette to the device palette while drawing + +LRESULT CBaseWindow::OnPaletteChange(HWND hwnd,UINT Message) +{ + // First check we are not changing the palette during closedown + + if (m_hwnd == NULL || hwnd == NULL) { + return (LRESULT) 0; + } + ASSERT(!m_bRealizing); + + // Should we realise our palette again + + if ((Message == WM_QUERYNEWPALETTE || hwnd != m_hwnd)) { + // It seems that even if we're invisible that we can get asked + // to realize our palette and this can cause really ugly side-effects + // Seems like there's another bug but this masks it a least for the + // shutting down case. + if (!IsWindowVisible(m_hwnd)) { + DbgLog((LOG_TRACE, 1, TEXT("Realizing when invisible!"))); + return (LRESULT) 0; + } + + // Avoid recursion with multiple graphs in the same app +#ifdef DEBUG + m_bRealizing = TRUE; +#endif + DoRealisePalette(Message != WM_QUERYNEWPALETTE); +#ifdef DEBUG + m_bRealizing = FALSE; +#endif + + // Should we redraw the window with the new palette + if (Message == WM_PALETTECHANGED) { + InvalidateRect(m_hwnd,NULL,FALSE); + } + } + + return (LRESULT) 1; +} + + +// Determine if the window exists. + +bool CBaseWindow::WindowExists() +{ + return !!IsWindow(m_hwnd); +} + + +// Return the default window rectangle + +RECT CBaseWindow::GetDefaultRect() +{ + RECT DefaultRect = {0,0,DEFWIDTH,DEFHEIGHT}; + ASSERT(m_hwnd); + // ASSERT(m_hdc); + return DefaultRect; +} + + +// Return the current window width + +LONG CBaseWindow::GetWindowWidth() +{ + ASSERT(m_hwnd); + // ASSERT(m_hdc); + return m_Width; +} + + +// Return the current window height + +LONG CBaseWindow::GetWindowHeight() +{ + ASSERT(m_hwnd); + // ASSERT(m_hdc); + return m_Height; +} + + +// Return the window handle + +HWND CBaseWindow::GetWindowHWND() +{ + ASSERT(m_hwnd); + // ASSERT(m_hdc); + return m_hwnd; +} + + +// Return the window drawing device context + +HDC CBaseWindow::GetWindowHDC() +{ + ASSERT(m_hwnd); + ASSERT(m_hdc); + return m_hdc; +} + + +// Return the offscreen window drawing device context + +HDC CBaseWindow::GetMemoryHDC() +{ + ASSERT(m_hwnd); + ASSERT(m_MemoryDC); + return m_MemoryDC; +} + + +#ifdef DEBUG +HPALETTE CBaseWindow::GetPalette() +{ + // The palette lock should always be held when accessing + // m_hPalette. + ASSERT(CritCheckIn(&m_PaletteLock)); + return m_hPalette; +} +#endif // DEBUG + + +// This is available to clients who want to change the window visiblity. It's +// little more than an indirection to the Win32 ShowWindow although these is +// some benefit in going through here as this function may change sometime + +HRESULT CBaseWindow::DoShowWindow(LONG ShowCmd) +{ + ShowWindow(m_hwnd,ShowCmd); + return NOERROR; +} + + +// Generate a WM_PAINT message for the video window + +void CBaseWindow::PaintWindow(BOOL bErase) +{ + InvalidateRect(m_hwnd,NULL,bErase); +} + + +// Allow an application to have us set the video window in the foreground. We +// have this because it is difficult for one thread to do do this to a window +// owned by another thread. Rather than expose the message we use to execute +// the inter thread send message we provide the interface function. All we do +// is to SendMessage to the video window renderer thread with a WM_SHOWSTAGE + +void CBaseWindow::DoSetWindowForeground(BOOL bFocus) +{ + SendMessage(m_hwnd,m_ShowStageMessage,(WPARAM) bFocus,(LPARAM) 0); +} + + +// Constructor initialises the owning object pointer. Since we are a worker +// class for the main window object we have relatively few state variables to +// look after. We are given device context handles to use later on as well as +// the source and destination rectangles (but reset them here just in case) + +CDrawImage::CDrawImage(CBaseWindow *pBaseWindow) : + m_pBaseWindow(pBaseWindow), + m_hdc(NULL), + m_MemoryDC(NULL), + m_bStretch(FALSE), + m_pMediaType(NULL), + m_bUsingImageAllocator(FALSE) +{ + ASSERT(pBaseWindow); + ResetPaletteVersion(); + SetRectEmpty(&m_TargetRect); + SetRectEmpty(&m_SourceRect); + + m_perfidRenderTime = MSR_REGISTER(TEXT("Single Blt time")); +} + + +// Overlay the image time stamps on the picture. Access to this method is +// serialised by the caller. We display the sample start and end times on +// top of the video using TextOut on the device context we are handed. If +// there isn't enough room in the window for the times we don't show them + +void CDrawImage::DisplaySampleTimes(IMediaSample *pSample) +{ +#ifdef DEBUG + // + // Only allow the "annoying" time messages if the users has turned the + // logging "way up" + // + BOOL bAccept = DbgCheckModuleLevel(LOG_TRACE, 5); + if (bAccept == FALSE) { + return; + } +#endif + + TCHAR szTimes[TIMELENGTH]; // Time stamp strings + ASSERT(pSample); // Quick sanity check + RECT ClientRect; // Client window size + SIZE Size; // Size of text output + + // Get the time stamps and window size + + pSample->GetTime((REFERENCE_TIME*)&m_StartSample, (REFERENCE_TIME*)&m_EndSample); + HWND hwnd = m_pBaseWindow->GetWindowHWND(); + EXECUTE_ASSERT(GetClientRect(hwnd,&ClientRect)); + + // Format the sample time stamps + + wsprintf(szTimes,TEXT("%08d : %08d"), + m_StartSample.Millisecs(), + m_EndSample.Millisecs()); + + ASSERT(lstrlen(szTimes) < TIMELENGTH); + + // Put the times in the middle at the bottom of the window + + GetTextExtentPoint32(m_hdc,szTimes,lstrlen(szTimes),&Size); + INT XPos = ((ClientRect.right - ClientRect.left) - Size.cx) / 2; + INT YPos = ((ClientRect.bottom - ClientRect.top) - Size.cy) * 4 / 5; + + // Check the window is big enough to have sample times displayed + + if ((XPos > 0) && (YPos > 0)) { + TextOut(m_hdc,XPos,YPos,szTimes,lstrlen(szTimes)); + } +} + + +// This is called when the drawing code sees that the image has a down level +// palette cookie. We simply call the SetDIBColorTable Windows API with the +// palette that is found after the BITMAPINFOHEADER - we return no errors + +void CDrawImage::UpdateColourTable(HDC hdc,BITMAPINFOHEADER *pbmi) +{ + ASSERT(pbmi->biClrUsed); + RGBQUAD *pColourTable = (RGBQUAD *)(pbmi+1); + + // Set the new palette in the device context + + UINT uiReturn = SetDIBColorTable(hdc,(UINT) 0, + pbmi->biClrUsed, + pColourTable); + + // Should always succeed but check in debug builds + ASSERT(uiReturn == pbmi->biClrUsed); +} + + +// No source rectangle scaling is done by the base class + +RECT CDrawImage::ScaleSourceRect(const RECT *pSource) +{ + ASSERT(pSource); + return *pSource; +} + + +// This is called when the funky output pin uses our allocator. The samples we +// allocate are special because the memory is shared between us and GDI thus +// removing one copy when we ask for the image to be rendered. The source type +// information is in the main renderer m_mtIn field which is initialised when +// the media type is agreed in SetMediaType, the media type may be changed on +// the fly if, for example, the source filter needs to change the palette + +void CDrawImage::FastRender(IMediaSample *pMediaSample) +{ + BITMAPINFOHEADER *pbmi; // Image format data + DIBDATA *pDibData; // Stores DIB information + BYTE *pImage; // Pointer to image data + HBITMAP hOldBitmap; // Store the old bitmap + CImageSample *pSample; // Pointer to C++ object + + ASSERT(m_pMediaType); + + // From the untyped source format block get the VIDEOINFO and subsequently + // the BITMAPINFOHEADER structure. We can cast the IMediaSample interface + // to a CImageSample object so we can retrieve it's DIBSECTION details + + pbmi = HEADER(m_pMediaType->Format()); + pSample = (CImageSample *) pMediaSample; + pDibData = pSample->GetDIBData(); + hOldBitmap = (HBITMAP) SelectObject(m_MemoryDC,pDibData->hBitmap); + + // Get a pointer to the real image data + + HRESULT hr = pMediaSample->GetPointer(&pImage); + if (FAILED(hr)) { + return; + } + + // Do we need to update the colour table, we increment our palette cookie + // each time we get a dynamic format change. The sample palette cookie is + // stored in the DIBDATA structure so we try to keep the fields in sync + // By the time we get to draw the images the format change will be done + // so all we do is ask the renderer for what it's palette version is + + if (pDibData->PaletteVersion < GetPaletteVersion()) { + ASSERT(pbmi->biBitCount <= iPALETTE); + UpdateColourTable(m_MemoryDC,pbmi); + pDibData->PaletteVersion = GetPaletteVersion(); + } + + // This allows derived classes to change the source rectangle that we do + // the drawing with. For example a renderer may ask a codec to stretch + // the video from 320x240 to 640x480, in which case the source we see in + // here will still be 320x240, although the source we want to draw with + // should be scaled up to 640x480. The base class implementation of this + // method does nothing but return the same rectangle as we are passed in + + RECT SourceRect = ScaleSourceRect(&m_SourceRect); + + // Is the window the same size as the video + + if (m_bStretch == FALSE) { + + // Put the image straight into the window + + BitBlt( + (HDC) m_hdc, // Target device HDC + m_TargetRect.left, // X sink position + m_TargetRect.top, // Y sink position + m_TargetRect.right - m_TargetRect.left, // Destination width + m_TargetRect.bottom - m_TargetRect.top, // Destination height + m_MemoryDC, // Source device context + SourceRect.left, // X source position + SourceRect.top, // Y source position + SRCCOPY); // Simple copy + + } else { + + // Stretch the image when copying to the window + + StretchBlt( + (HDC) m_hdc, // Target device HDC + m_TargetRect.left, // X sink position + m_TargetRect.top, // Y sink position + m_TargetRect.right - m_TargetRect.left, // Destination width + m_TargetRect.bottom - m_TargetRect.top, // Destination height + m_MemoryDC, // Source device HDC + SourceRect.left, // X source position + SourceRect.top, // Y source position + SourceRect.right - SourceRect.left, // Source width + SourceRect.bottom - SourceRect.top, // Source height + SRCCOPY); // Simple copy + } + + // This displays the sample times over the top of the image. This used to + // draw the times into the offscreen device context however that actually + // writes the text into the image data buffer which may not be writable + + #ifdef DEBUG + DisplaySampleTimes(pMediaSample); + #endif + + // Put the old bitmap back into the device context so we don't leak + SelectObject(m_MemoryDC,hOldBitmap); +} + + +// This is called when there is a sample ready to be drawn, unfortunately the +// output pin was being rotten and didn't choose our super excellent shared +// memory DIB allocator so we have to do this slow render using boring old GDI +// SetDIBitsToDevice and StretchDIBits. The down side of using these GDI +// functions is that the image data has to be copied across from our address +// space into theirs before going to the screen (although in reality the cost +// is small because all they do is to map the buffer into their address space) + +void CDrawImage::SlowRender(IMediaSample *pMediaSample) +{ + // Get the BITMAPINFOHEADER for the connection + + ASSERT(m_pMediaType); + BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format()); + BYTE *pImage; + + // Get the image data buffer + + HRESULT hr = pMediaSample->GetPointer(&pImage); + if (FAILED(hr)) { + return; + } + + // This allows derived classes to change the source rectangle that we do + // the drawing with. For example a renderer may ask a codec to stretch + // the video from 320x240 to 640x480, in which case the source we see in + // here will still be 320x240, although the source we want to draw with + // should be scaled up to 640x480. The base class implementation of this + // method does nothing but return the same rectangle as we are passed in + + RECT SourceRect = ScaleSourceRect(&m_SourceRect); + + LONG lAdjustedSourceTop = SourceRect.top; + // if the origin of bitmap is bottom-left, adjust soruce_rect_top + // to be the bottom-left corner instead of the top-left. + if (pbmi->biHeight > 0) { + lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom; + } + // Is the window the same size as the video + + if (m_bStretch == FALSE) { + + // Put the image straight into the window + + SetDIBitsToDevice( + (HDC) m_hdc, // Target device HDC + m_TargetRect.left, // X sink position + m_TargetRect.top, // Y sink position + m_TargetRect.right - m_TargetRect.left, // Destination width + m_TargetRect.bottom - m_TargetRect.top, // Destination height + SourceRect.left, // X source position + lAdjustedSourceTop, // Adjusted Y source position + (UINT) 0, // Start scan line + pbmi->biHeight, // Scan lines present + pImage, // Image data + (BITMAPINFO *) pbmi, // DIB header + DIB_RGB_COLORS); // Type of palette + + } else { + + // Stretch the image when copying to the window + + StretchDIBits( + (HDC) m_hdc, // Target device HDC + m_TargetRect.left, // X sink position + m_TargetRect.top, // Y sink position + m_TargetRect.right - m_TargetRect.left, // Destination width + m_TargetRect.bottom - m_TargetRect.top, // Destination height + SourceRect.left, // X source position + lAdjustedSourceTop, // Adjusted Y source position + SourceRect.right - SourceRect.left, // Source width + SourceRect.bottom - SourceRect.top, // Source height + pImage, // Image data + (BITMAPINFO *) pbmi, // DIB header + DIB_RGB_COLORS, // Type of palette + SRCCOPY); // Simple image copy + } + + // This shows the sample reference times over the top of the image which + // looks a little flickery. I tried using GdiSetBatchLimit and GdiFlush to + // control the screen updates but it doesn't quite work as expected and + // only partially reduces the flicker. I also tried using a memory context + // and combining the two in that before doing a final BitBlt operation to + // the screen, unfortunately this has considerable performance penalties + // and also means that this code is not executed when compiled retail + + #ifdef DEBUG + DisplaySampleTimes(pMediaSample); + #endif +} + + +// This is called with an IMediaSample interface on the image to be drawn. We +// decide on the drawing mechanism based on who's allocator we are using. We +// may be called when the window wants an image painted by WM_PAINT messages +// We can't realise the palette here because we have the renderer lock, any +// call to realise may cause an interthread send message to the window thread +// which may in turn be waiting to get the renderer lock before servicing it + +BOOL CDrawImage::DrawImage(IMediaSample *pMediaSample) +{ + ASSERT(m_hdc); + ASSERT(m_MemoryDC); + NotifyStartDraw(); + + // If the output pin used our allocator then the samples passed are in + // fact CVideoSample objects that contain CreateDIBSection data that we + // use to do faster image rendering, they may optionally also contain a + // DirectDraw surface pointer in which case we do not do the drawing + + if (m_bUsingImageAllocator == FALSE) { + SlowRender(pMediaSample); + EXECUTE_ASSERT(GdiFlush()); + NotifyEndDraw(); + return TRUE; + } + + // This is a DIBSECTION buffer + + FastRender(pMediaSample); + EXECUTE_ASSERT(GdiFlush()); + NotifyEndDraw(); + return TRUE; +} + + +BOOL CDrawImage::DrawVideoImageHere( + HDC hdc, + IMediaSample *pMediaSample, + LPRECT lprcSrc, + LPRECT lprcDst + ) +{ + ASSERT(m_pMediaType); + BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format()); + BYTE *pImage; + + // Get the image data buffer + + HRESULT hr = pMediaSample->GetPointer(&pImage); + if (FAILED(hr)) { + return FALSE; + } + + RECT SourceRect; + RECT TargetRect; + + if (lprcSrc) + SourceRect = *lprcSrc; + else + SourceRect = ScaleSourceRect(&m_SourceRect); + + if (lprcDst) + TargetRect = *lprcDst; + else + TargetRect = m_TargetRect; + + LONG lAdjustedSourceTop = SourceRect.top; + // if the origin of bitmap is bottom-left, adjust soruce_rect_top + // to be the bottom-left corner instead of the top-left. + if (pbmi->biHeight > 0) { + lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom; + } + + + // Stretch the image when copying to the DC + + BOOL bRet = (0 != StretchDIBits(hdc, + TargetRect.left, + TargetRect.top, + TargetRect.right - TargetRect.left, + TargetRect.bottom - TargetRect.top, + SourceRect.left, + lAdjustedSourceTop, + SourceRect.right - SourceRect.left, + SourceRect.bottom - SourceRect.top, + pImage, + (BITMAPINFO *)pbmi, + DIB_RGB_COLORS, + SRCCOPY)); + return bRet; +} + + +// This is called by the owning window object after it has created the window +// and it's drawing contexts. We are constructed with the base window we'll +// be drawing into so when given the notification we retrive the device HDCs +// to draw with. We cannot call these in our constructor as they are virtual + +void CDrawImage::SetDrawContext() +{ + m_MemoryDC = m_pBaseWindow->GetMemoryHDC(); + m_hdc = m_pBaseWindow->GetWindowHDC(); +} + + +// This is called to set the target rectangle in the video window, it will be +// called whenever a WM_SIZE message is retrieved from the message queue. We +// simply store the rectangle and use it later when we do the drawing calls + +void CDrawImage::SetTargetRect(RECT *pTargetRect) +{ + ASSERT(pTargetRect); + m_TargetRect = *pTargetRect; + SetStretchMode(); +} + + +// Return the current target rectangle + +void CDrawImage::GetTargetRect(RECT *pTargetRect) +{ + ASSERT(pTargetRect); + *pTargetRect = m_TargetRect; +} + + +// This is called when we want to change the section of the image to draw. We +// use this information in the drawing operation calls later on. We must also +// see if the source and destination rectangles have the same dimensions. If +// not we must stretch during the drawing rather than a direct pixel copy + +void CDrawImage::SetSourceRect(RECT *pSourceRect) +{ + ASSERT(pSourceRect); + m_SourceRect = *pSourceRect; + SetStretchMode(); +} + + +// Return the current source rectangle + +void CDrawImage::GetSourceRect(RECT *pSourceRect) +{ + ASSERT(pSourceRect); + *pSourceRect = m_SourceRect; +} + + +// This is called when either the source or destination rectanges change so we +// can update the stretch flag. If the rectangles don't match we stretch the +// video during the drawing otherwise we call the fast pixel copy functions +// NOTE the source and/or the destination rectangle may be completely empty + +void CDrawImage::SetStretchMode() +{ + // Calculate the overall rectangle dimensions + + LONG SourceWidth = m_SourceRect.right - m_SourceRect.left; + LONG SinkWidth = m_TargetRect.right - m_TargetRect.left; + LONG SourceHeight = m_SourceRect.bottom - m_SourceRect.top; + LONG SinkHeight = m_TargetRect.bottom - m_TargetRect.top; + + m_bStretch = TRUE; + if (SourceWidth == SinkWidth) { + if (SourceHeight == SinkHeight) { + m_bStretch = FALSE; + } + } +} + + +// Tell us whose allocator we are using. This should be called with TRUE if +// the filter agrees to use an allocator based around the CImageAllocator +// SDK base class - whose image buffers are made through CreateDIBSection. +// Otherwise this should be called with FALSE and we will draw the images +// using SetDIBitsToDevice and StretchDIBitsToDevice. None of these calls +// can handle buffers which have non zero strides (like DirectDraw uses) + +void CDrawImage::NotifyAllocator(BOOL bUsingImageAllocator) +{ + m_bUsingImageAllocator = bUsingImageAllocator; +} + + +// Are we using the image DIBSECTION allocator + +BOOL CDrawImage::UsingImageAllocator() +{ + return m_bUsingImageAllocator; +} + + +// We need the media type of the connection so that we can get the BITMAPINFO +// from it. We use that in the calls to draw the image such as StretchDIBits +// and also when updating the colour table held in shared memory DIBSECTIONs + +void CDrawImage::NotifyMediaType(CMediaType *pMediaType) +{ + m_pMediaType = pMediaType; +} + + +// We store in this object a cookie maintaining the current palette version. +// Each time a palettised format is changed we increment this value so that +// when we come to draw the images we look at the colour table value they +// have and if less than the current we know to update it. This version is +// only needed and indeed used when working with shared memory DIBSECTIONs + +LONG CDrawImage::GetPaletteVersion() +{ + return m_PaletteVersion; +} + + +// Resets the current palette version number + +void CDrawImage::ResetPaletteVersion() +{ + m_PaletteVersion = PALETTE_VERSION; +} + + +// Increment the current palette version + +void CDrawImage::IncrementPaletteVersion() +{ + m_PaletteVersion++; +} + + +// Constructor must initialise the base allocator. Each sample we create has a +// palette version cookie on board. When the source filter changes the palette +// during streaming the window object increments an internal cookie counter it +// keeps as well. When it comes to render the samples it looks at the cookie +// values and if they don't match then it knows to update the sample's colour +// table. However we always create samples with a cookie of PALETTE_VERSION +// If there have been multiple format changes and we disconnect and reconnect +// thereby causing the samples to be reallocated we will create them with a +// cookie much lower than the current version, this isn't a problem since it +// will be seen by the window object and the versions will then be updated + +CImageAllocator::CImageAllocator(CBaseFilter *pFilter, + TCHAR *pName, + HRESULT *phr) : + CBaseAllocator(pName,NULL,phr,TRUE,TRUE), + m_pFilter(pFilter) +{ + ASSERT(phr); + ASSERT(pFilter); +} + + +// Check our DIB buffers have been released + +#ifdef DEBUG +CImageAllocator::~CImageAllocator() +{ + ASSERT(m_bCommitted == FALSE); +} +#endif + + +// Called from destructor and also from base class to free resources. We work +// our way through the list of media samples deleting the DIBSECTION created +// for each. All samples should be back in our list so there is no chance a +// filter is still using one to write on the display or hold on a pending list + +void CImageAllocator::Free() +{ + ASSERT(m_lAllocated == m_lFree.GetCount()); + EXECUTE_ASSERT(GdiFlush()); + CImageSample *pSample; + DIBDATA *pDibData; + + while (m_lFree.GetCount() != 0) { + pSample = (CImageSample *) m_lFree.RemoveHead(); + pDibData = pSample->GetDIBData(); + EXECUTE_ASSERT(DeleteObject(pDibData->hBitmap)); + EXECUTE_ASSERT(CloseHandle(pDibData->hMapping)); + delete pSample; + } + + m_lAllocated = 0; +} + + +// Prepare the allocator by checking all the input parameters + +STDMETHODIMP CImageAllocator::CheckSizes(ALLOCATOR_PROPERTIES *pRequest) +{ + // Check we have a valid connection + + if (m_pMediaType == NULL) { + return VFW_E_NOT_CONNECTED; + } + + // NOTE We always create a DIB section with the source format type which + // may contain a source palette. When we do the BitBlt drawing operation + // the target display device may contain a different palette (we may not + // have the focus) in which case GDI will do after the palette mapping + + VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *) m_pMediaType->Format(); + + // When we call CreateDIBSection it implicitly maps only enough memory + // for the image as defined by thee BITMAPINFOHEADER. If the user asks + // for an image smaller than this then we reject the call, if they ask + // for an image larger than this then we return what they can have + + if ((DWORD) pRequest->cbBuffer < pVideoInfo->bmiHeader.biSizeImage) { + return E_INVALIDARG; + } + + // Reject buffer prefixes + + if (pRequest->cbPrefix > 0) { + return E_INVALIDARG; + } + + pRequest->cbBuffer = pVideoInfo->bmiHeader.biSizeImage; + return NOERROR; +} + + +// Agree the number of media sample buffers and their sizes. The base class +// this allocator is derived from allows samples to be aligned only on byte +// boundaries NOTE the buffers are not allocated until the Commit call + +STDMETHODIMP CImageAllocator::SetProperties( + ALLOCATOR_PROPERTIES * pRequest, + ALLOCATOR_PROPERTIES * pActual) +{ + ALLOCATOR_PROPERTIES Adjusted = *pRequest; + + // Check the parameters fit with the current connection + + HRESULT hr = CheckSizes(&Adjusted); + if (FAILED(hr)) { + return hr; + } + return CBaseAllocator::SetProperties(&Adjusted, pActual); +} + + +// Commit the memory by allocating the agreed number of media samples. For +// each sample we are committed to creating we have a CImageSample object +// that we use to manage it's resources. This is initialised with a DIBDATA +// structure that contains amongst other things the GDI DIBSECTION handle +// We will access the renderer media type during this so we must have locked +// (to prevent the format changing for example). The class overrides Commit +// and Decommit to do this locking (base class Commit in turn calls Alloc) + +HRESULT CImageAllocator::Alloc(void) +{ + ASSERT(m_pMediaType); + CImageSample *pSample; + DIBDATA DibData; + + // Check the base allocator says it's ok to continue + + HRESULT hr = CBaseAllocator::Alloc(); + if (FAILED(hr)) { + return hr; + } + + // We create a new memory mapped object although we don't map it into our + // address space because GDI does that in CreateDIBSection. It is possible + // that we run out of resources before creating all the samples in which + // case the available sample list is left with those already created + + ASSERT(m_lAllocated == 0); + while (m_lAllocated < m_lCount) { + + // Create and initialise a shared memory GDI buffer + + HRESULT hr = CreateDIB(m_lSize,DibData); + if (FAILED(hr)) { + return hr; + } + + // Create the sample object and pass it the DIBDATA + + pSample = CreateImageSample(DibData.pBase,m_lSize); + if (pSample == NULL) { + EXECUTE_ASSERT(DeleteObject(DibData.hBitmap)); + EXECUTE_ASSERT(CloseHandle(DibData.hMapping)); + return E_OUTOFMEMORY; + } + + // Add the completed sample to the available list + + pSample->SetDIBData(&DibData); + m_lFree.Add(pSample); + m_lAllocated++; + } + return NOERROR; +} + + +// We have a virtual method that allocates the samples so that a derived class +// may override it and allocate more specialised sample objects. So long as it +// derives its samples from CImageSample then all this code will still work ok + +CImageSample *CImageAllocator::CreateImageSample(LPBYTE pData,LONG Length) +{ + HRESULT hr = NOERROR; + CImageSample *pSample; + + // Allocate the new sample and check the return codes + + pSample = new CImageSample((CBaseAllocator *) this, // Base class + NAME("Video sample"), // DEBUG name + (HRESULT *) &hr, // Return code + (LPBYTE) pData, // DIB address + (LONG) Length); // Size of DIB + + if (pSample == NULL || FAILED(hr)) { + delete pSample; + return NULL; + } + return pSample; +} + + +// This function allocates a shared memory block for use by the source filter +// generating DIBs for us to render. The memory block is created in shared +// memory so that GDI doesn't have to copy the memory when we do a BitBlt + +HRESULT CImageAllocator::CreateDIB(LONG InSize,DIBDATA &DibData) +{ + BITMAPINFO *pbmi; // Format information for pin + BYTE *pBase; // Pointer to the actual image + HANDLE hMapping; // Handle to mapped object + HBITMAP hBitmap; // DIB section bitmap handle + + // Create a file mapping object and map into our address space + + hMapping = CreateFileMapping(hMEMORY, // Use system page file + NULL, // No security attributes + PAGE_READWRITE, // Full access to memory + (DWORD) 0, // Less than 4Gb in size + InSize, // Size of buffer + NULL); // No name to section + if (hMapping == NULL) { + DWORD Error = GetLastError(); + return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, Error); + } + + // NOTE We always create a DIB section with the source format type which + // may contain a source palette. When we do the BitBlt drawing operation + // the target display device may contain a different palette (we may not + // have the focus) in which case GDI will do after the palette mapping + + pbmi = (BITMAPINFO *) HEADER(m_pMediaType->Format()); + if (m_pMediaType == NULL) { + DbgBreak("Invalid media type"); + } + + hBitmap = CreateDIBSection((HDC) NULL, // NO device context + pbmi, // Format information + DIB_RGB_COLORS, // Use the palette + (VOID **) &pBase, // Pointer to image data + hMapping, // Mapped memory handle + (DWORD) 0); // Offset into memory + + if (hBitmap == NULL || pBase == NULL) { + EXECUTE_ASSERT(CloseHandle(hMapping)); + DWORD Error = GetLastError(); + return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, Error); + } + + // Initialise the DIB information structure + + DibData.hBitmap = hBitmap; + DibData.hMapping = hMapping; + DibData.pBase = pBase; + DibData.PaletteVersion = PALETTE_VERSION; + GetObject(hBitmap,sizeof(DIBSECTION),(VOID *)&DibData.DibSection); + + return NOERROR; +} + + +// We use the media type during the DIBSECTION creation + +void CImageAllocator::NotifyMediaType(CMediaType *pMediaType) +{ + m_pMediaType = pMediaType; +} + + +// Overriden to increment the owning object's reference count + +STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingAddRef() +{ + return m_pFilter->AddRef(); +} + + +// Overriden to decrement the owning object's reference count + +STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingRelease() +{ + return m_pFilter->Release(); +} + + +// If you derive a class from CMediaSample that has to transport specialised +// member variables and entry points then there are three alternate solutions +// The first is to create a memory buffer larger than actually required by the +// sample and store your information either at the beginning of it or at the +// end, the former being moderately safer allowing for misbehaving transform +// filters. You then adjust the buffer address when you create the base media +// sample. This has the disadvantage of breaking up the memory allocated to +// the samples into separate blocks. The second solution is to implement a +// class derived from CMediaSample and support additional interface(s) that +// convey your private data. This means defining a custom interface. The final +// alternative is to create a class that inherits from CMediaSample and adds +// the private data structures, when you get an IMediaSample in your Receive() +// call check to see if your allocator is being used, and if it is then cast +// the IMediaSample into one of your objects. Additional checks can be made +// to ensure the sample's this pointer is known to be one of your own objects + +CImageSample::CImageSample(CBaseAllocator *pAllocator, + TCHAR *pName, + HRESULT *phr, + LPBYTE pBuffer, + LONG length) : + CMediaSample(pName,pAllocator,phr,pBuffer,length), + m_bInit(FALSE) +{ + ASSERT(pAllocator); + ASSERT(pBuffer); +} + + +// Set the shared memory DIB information + +void CImageSample::SetDIBData(DIBDATA *pDibData) +{ + ASSERT(pDibData); + m_DibData = *pDibData; + m_bInit = TRUE; +} + + +// Retrieve the shared memory DIB data + +DIBDATA *CImageSample::GetDIBData() +{ + ASSERT(m_bInit == TRUE); + return &m_DibData; +} + + +// This class handles the creation of a palette. It is fairly specialist and +// is intended to simplify palette management for video renderer filters. It +// is for this reason that the constructor requires three other objects with +// which it interacts, namely a base media filter, a base window and a base +// drawing object although the base window or the draw object may be NULL to +// ignore that part of us. We try not to create and install palettes unless +// absolutely necessary as they typically require WM_PALETTECHANGED messages +// to be sent to every window thread in the system which is very expensive + +CImagePalette::CImagePalette(CBaseFilter *pBaseFilter, + CBaseWindow *pBaseWindow, + CDrawImage *pDrawImage) : + m_pBaseWindow(pBaseWindow), + m_pFilter(pBaseFilter), + m_pDrawImage(pDrawImage), + m_hPalette(NULL) +{ + ASSERT(m_pFilter); +} + + +// Destructor + +#ifdef DEBUG +CImagePalette::~CImagePalette() +{ + ASSERT(m_hPalette == NULL); +} +#endif + + +// We allow dynamic format changes of the palette but rather than change the +// palette every time we call this to work out whether an update is required. +// If the original type didn't use a palette and the new one does (or vica +// versa) then we return TRUE. If neither formats use a palette we'll return +// FALSE. If both formats use a palette we compare their colours and return +// FALSE if they match. This therefore short circuits palette creation unless +// absolutely necessary since installing palettes is an expensive operation + +BOOL CImagePalette::ShouldUpdate(const VIDEOINFOHEADER *pNewInfo, + const VIDEOINFOHEADER *pOldInfo) +{ + // We may not have a current format yet + + if (pOldInfo == NULL) { + return TRUE; + } + + // Do both formats not require a palette + + if (ContainsPalette(pNewInfo) == FALSE) { + if (ContainsPalette(pOldInfo) == FALSE) { + return FALSE; + } + } + + // Compare the colours to see if they match + + DWORD VideoEntries = pNewInfo->bmiHeader.biClrUsed; + if (ContainsPalette(pNewInfo) == TRUE) + if (ContainsPalette(pOldInfo) == TRUE) + if (pOldInfo->bmiHeader.biClrUsed == VideoEntries) + if (pOldInfo->bmiHeader.biClrUsed > 0) + if (memcmp((PVOID) GetBitmapPalette(pNewInfo), + (PVOID) GetBitmapPalette(pOldInfo), + VideoEntries * sizeof(RGBQUAD)) == 0) { + + return FALSE; + } + return TRUE; +} + + +// This is normally called when the input pin type is set to install a palette +// We will typically be called from two different places. The first is when we +// have negotiated a palettised media type after connection, the other is when +// we receive a new type during processing with an updated palette in which +// case we must remove and release the resources held by the current palette + +// We can be passed an optional device name if we wish to prepare a palette +// for a specific monitor on a multi monitor system + +HRESULT CImagePalette::PreparePalette(const CMediaType *pmtNew, + const CMediaType *pmtOld, + LPSTR szDevice) +{ + const VIDEOINFOHEADER *pNewInfo = (VIDEOINFOHEADER *) pmtNew->Format(); + const VIDEOINFOHEADER *pOldInfo = (VIDEOINFOHEADER *) pmtOld->Format(); + ASSERT(pNewInfo); + + // This is an performance optimisation, when we get a media type we check + // to see if the format requires a palette change. If either we need one + // when previously we didn't or vica versa then this returns TRUE, if we + // previously needed a palette and we do now it compares their colours + + if (ShouldUpdate(pNewInfo,pOldInfo) == FALSE) { + NOTE("No update needed"); + return S_FALSE; + } + + // We must notify the filter graph that the application may have changed + // the palette although in practice we don't bother checking to see if it + // is really different. If it tries to get the palette either the window + // or renderer lock will ensure it doesn't get in until we are finished + + RemovePalette(); + m_pFilter->NotifyEvent(EC_PALETTE_CHANGED,0,0); + + // Do we need a palette for the new format + + if (ContainsPalette(pNewInfo) == FALSE) { + NOTE("New has no palette"); + return S_FALSE; + } + + if (m_pBaseWindow) { + m_pBaseWindow->LockPaletteLock(); + } + + // If we're changing the palette on the fly then we increment our palette + // cookie which is compared against the cookie also stored in all of our + // DIBSECTION media samples. If they don't match when we come to draw it + // then we know the sample is out of date and we'll update it's palette + + NOTE("Making new colour palette"); + m_hPalette = MakePalette(pNewInfo, szDevice); + ASSERT(m_hPalette != NULL); + + if (m_pBaseWindow) { + m_pBaseWindow->UnlockPaletteLock(); + } + + // The window in which the new palette is to be realised may be a NULL + // pointer to signal that no window is in use, if so we don't call it + // Some filters just want to use this object to create/manage palettes + + if (m_pBaseWindow) m_pBaseWindow->SetPalette(m_hPalette); + + // This is the only time where we need access to the draw object to say + // to it that a new palette will be arriving on a sample real soon. The + // constructor may take a NULL pointer in which case we don't call this + + if (m_pDrawImage) m_pDrawImage->IncrementPaletteVersion(); + return NOERROR; +} + + +// Helper function to copy a palette out of any kind of VIDEOINFO (ie it may +// be YUV or true colour) into a palettised VIDEOINFO. We use this changing +// palettes on DirectDraw samples as a source filter can attach a palette to +// any buffer (eg YUV) and hand it back. We make a new palette out of that +// format and then copy the palette colours into the current connection type + +HRESULT CImagePalette::CopyPalette(const CMediaType *pSrc,CMediaType *pDest) +{ + // Reset the destination palette before starting + + VIDEOINFOHEADER *pDestInfo = (VIDEOINFOHEADER *) pDest->Format(); + pDestInfo->bmiHeader.biClrUsed = 0; + pDestInfo->bmiHeader.biClrImportant = 0; + + // Does the destination have a palette + + if (PALETTISED(pDestInfo) == FALSE) { + NOTE("No destination palette"); + return S_FALSE; + } + + // Does the source contain a palette + + const VIDEOINFOHEADER *pSrcInfo = (VIDEOINFOHEADER *) pSrc->Format(); + if (ContainsPalette(pSrcInfo) == FALSE) { + NOTE("No source palette"); + return S_FALSE; + } + + // The number of colours may be zero filled + + DWORD PaletteEntries = pSrcInfo->bmiHeader.biClrUsed; + if (PaletteEntries == 0) { + DWORD Maximum = (1 << pSrcInfo->bmiHeader.biBitCount); + NOTE1("Setting maximum colours (%d)",Maximum); + PaletteEntries = Maximum; + } + + // Make sure the destination has enough room for the palette + + ASSERT(pSrcInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS); + ASSERT(pSrcInfo->bmiHeader.biClrImportant <= PaletteEntries); + ASSERT(COLORS(pDestInfo) == GetBitmapPalette(pDestInfo)); + pDestInfo->bmiHeader.biClrUsed = PaletteEntries; + pDestInfo->bmiHeader.biClrImportant = pSrcInfo->bmiHeader.biClrImportant; + ULONG BitmapSize = GetBitmapFormatSize(HEADER(pSrcInfo)); + + if (pDest->FormatLength() < BitmapSize) { + NOTE("Reallocating destination"); + pDest->ReallocFormatBuffer(BitmapSize); + } + + // Now copy the palette colours across + + CopyMemory((PVOID) COLORS(pDestInfo), + (PVOID) GetBitmapPalette(pSrcInfo), + PaletteEntries * sizeof(RGBQUAD)); + + return NOERROR; +} + + +// This is normally called when the palette is changed (typically during a +// dynamic format change) to remove any palette we previously installed. We +// replace it (if necessary) in the video window with a standard VGA palette +// that should always be available even if this is a true colour display + +HRESULT CImagePalette::RemovePalette() +{ + if (m_pBaseWindow) { + m_pBaseWindow->LockPaletteLock(); + } + + // Do we have a palette to remove + + if (m_hPalette != NULL) { + + if (m_pBaseWindow) { + // Make sure that the window's palette handle matches + // our palette handle. + ASSERT(m_hPalette == m_pBaseWindow->GetPalette()); + + m_pBaseWindow->UnsetPalette(); + } + + EXECUTE_ASSERT(DeleteObject(m_hPalette)); + m_hPalette = NULL; + } + + if (m_pBaseWindow) { + m_pBaseWindow->UnlockPaletteLock(); + } + + return NOERROR; +} + + +// Called to create a palette for the object, the data structure used by GDI +// to describe a palette is a LOGPALETTE, this includes a variable number of +// PALETTEENTRY fields which are the colours, we have to convert the RGBQUAD +// colour fields we are handed in a BITMAPINFO from the media type into these +// This handles extraction of palettes from true colour and YUV media formats + +// We can be passed an optional device name if we wish to prepare a palette +// for a specific monitor on a multi monitor system + +HPALETTE CImagePalette::MakePalette(const VIDEOINFOHEADER *pVideoInfo, LPSTR szDevice) +{ + ASSERT(ContainsPalette(pVideoInfo) == TRUE); + ASSERT(pVideoInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS); + BITMAPINFOHEADER *pHeader = HEADER(pVideoInfo); + + const RGBQUAD *pColours; // Pointer to the palette + LOGPALETTE *lp; // Used to create a palette + HPALETTE hPalette; // Logical palette object + + lp = (LOGPALETTE *) new BYTE[sizeof(LOGPALETTE) + SIZE_PALETTE]; + if (lp == NULL) { + return NULL; + } + + // Unfortunately for some hare brained reason a GDI palette entry (a + // PALETTEENTRY structure) is different to a palette entry from a DIB + // format (a RGBQUAD structure) so we have to do the field conversion + // The VIDEOINFO containing the palette may be a true colour type so + // we use GetBitmapPalette to skip over any bit fields if they exist + + lp->palVersion = PALVERSION; + lp->palNumEntries = (USHORT) pHeader->biClrUsed; + if (lp->palNumEntries == 0) + lp->palNumEntries = (WORD) (1 << pHeader->biBitCount); + pColours = GetBitmapPalette(pVideoInfo); + + for (DWORD dwCount = 0;dwCount < lp->palNumEntries;dwCount++) { + lp->palPalEntry[dwCount].peRed = pColours[dwCount].rgbRed; + lp->palPalEntry[dwCount].peGreen = pColours[dwCount].rgbGreen; + lp->palPalEntry[dwCount].peBlue = pColours[dwCount].rgbBlue; + lp->palPalEntry[dwCount].peFlags = 0; + } + + MakeIdentityPalette(lp->palPalEntry, lp->palNumEntries, szDevice); + + // Create a logical palette + + hPalette = CreatePalette(lp); + ASSERT(hPalette != NULL); + delete[] lp; + return hPalette; +} + + +// GDI does a fair job of compressing the palette entries you give it, so for +// example if you have five entries with an RGB colour (0,0,0) it will remove +// all but one of them. When you subsequently draw an image it will map from +// your logical palette to the compressed device palette. This function looks +// to see if it is trying to be an identity palette and if so sets the flags +// field in the PALETTEENTRYs so they remain expanded to boost performance + +// We can be passed an optional device name if we wish to prepare a palette +// for a specific monitor on a multi monitor system + +HRESULT CImagePalette::MakeIdentityPalette(PALETTEENTRY *pEntry,INT iColours, LPSTR szDevice) +{ + PALETTEENTRY SystemEntries[10]; // System palette entries + BOOL bIdentityPalette = TRUE; // Is an identity palette + ASSERT(iColours <= iPALETTE_COLORS); // Should have a palette + const int PalLoCount = 10; // First ten reserved colours + const int PalHiStart = 246; // Last VGA palette entries + + // Does this have the full colour range + + if (iColours < 10) { + return S_FALSE; + } + + // Apparently some displays have odd numbers of system colours + + // Get a DC on the right monitor - it's ugly, but this is the way you have + // to do it + HDC hdc; + if (szDevice == NULL || lstrcmpiA(szDevice, "DISPLAY") == 0) + hdc = CreateDCA("DISPLAY", NULL, NULL, NULL); + else + hdc = CreateDCA(NULL, szDevice, NULL, NULL); + if (NULL == hdc) { + return E_OUTOFMEMORY; + } + INT Reserved = GetDeviceCaps(hdc,NUMRESERVED); + if (Reserved != 20) { + DeleteDC(hdc); + return S_FALSE; + } + + // Compare our palette against the first ten system entries. The reason I + // don't do a memory compare between our two arrays of colours is because + // I am not sure what will be in the flags fields for the system entries + + UINT Result = GetSystemPaletteEntries(hdc,0,PalLoCount,SystemEntries); + for (UINT Count = 0;Count < Result;Count++) { + if (SystemEntries[Count].peRed != pEntry[Count].peRed || + SystemEntries[Count].peGreen != pEntry[Count].peGreen || + SystemEntries[Count].peBlue != pEntry[Count].peBlue) { + bIdentityPalette = FALSE; + } + } + + // And likewise compare against the last ten entries + + Result = GetSystemPaletteEntries(hdc,PalHiStart,PalLoCount,SystemEntries); + for (Count = 0;Count < Result;Count++) { + if (INT(Count) + PalHiStart < iColours) { + if (SystemEntries[Count].peRed != pEntry[PalHiStart + Count].peRed || + SystemEntries[Count].peGreen != pEntry[PalHiStart + Count].peGreen || + SystemEntries[Count].peBlue != pEntry[PalHiStart + Count].peBlue) { + bIdentityPalette = FALSE; + } + } + } + + // If not an identity palette then return S_FALSE + + DeleteDC(hdc); + if (bIdentityPalette == FALSE) { + return S_FALSE; + } + + // Set the non VGA entries so that GDI doesn't map them + + for (Count = PalLoCount;INT(Count) < min(PalHiStart,iColours);Count++) { + pEntry[Count].peFlags = PC_NOCOLLAPSE; + } + return NOERROR; +} + + +// Constructor initialises the VIDEOINFO we keep storing the current display +// format. The format can be changed at any time, to reset the format held +// by us call the RefreshDisplayType directly (it's a public method). Since +// more than one thread will typically call us (ie window threads resetting +// the type and source threads in the type checking methods) we have a lock + +CImageDisplay::CImageDisplay() +{ + RefreshDisplayType(NULL); +} + + + +// This initialises the format we hold which contains the display device type +// We do a conversion on the display device type in here so that when we start +// type checking input formats we can assume that certain fields have been set +// correctly, an example is when we make the 16 bit mask fields explicit. This +// is normally called when we receive WM_DEVMODECHANGED device change messages + +// The optional szDeviceName parameter tells us which monitor we are interested +// in for a multi monitor system + +HRESULT CImageDisplay::RefreshDisplayType(LPSTR szDeviceName) +{ + CAutoLock cDisplayLock(this); + + // Set the preferred format type + + ZeroMemory((PVOID)&m_Display,sizeof(VIDEOINFOHEADER)+sizeof(TRUECOLORINFO)); + m_Display.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + m_Display.bmiHeader.biBitCount = FALSE; + + // Get the bit depth of a device compatible bitmap + + // get caps of whichever monitor they are interested in (multi monitor) + HDC hdcDisplay; + // it's ugly, but this is the way you have to do it + if (szDeviceName == NULL || lstrcmpiA(szDeviceName, "DISPLAY") == 0) + hdcDisplay = CreateDCA("DISPLAY", NULL, NULL, NULL); + else + hdcDisplay = CreateDCA(NULL, szDeviceName, NULL, NULL); + if (hdcDisplay == NULL) { + ASSERT(FALSE); + DbgLog((LOG_ERROR,1,TEXT("ACK! Can't get a DC for %hs"), + szDeviceName ? szDeviceName : "")); + return E_FAIL; + } else { + DbgLog((LOG_TRACE,3,TEXT("Created a DC for %s"), + szDeviceName ? szDeviceName : "")); + } + HBITMAP hbm = CreateCompatibleBitmap(hdcDisplay,1,1); + if ( hbm ) + { + GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS); + + // This call will get the colour table or the proper bitfields + GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS); + DeleteObject(hbm); + } + DeleteDC(hdcDisplay); + + // Complete the display type initialisation + + ASSERT(CheckHeaderValidity(&m_Display)); + UpdateFormat(&m_Display); + DbgLog((LOG_TRACE,3,TEXT("New DISPLAY bit depth =%d"), + m_Display.bmiHeader.biBitCount)); + return NOERROR; +} + + +// We assume throughout this code that any bitfields masks are allowed no +// more than eight bits to store a colour component. This checks that the +// bit count assumption is enforced and also makes sure that all the bits +// set are contiguous. We return a boolean TRUE if the field checks out ok + +BOOL CImageDisplay::CheckBitFields(const VIDEOINFO *pInput) +{ + DWORD *pBitFields = (DWORD *) BITMASKS(pInput); + + for (INT iColour = iRED;iColour <= iBLUE;iColour++) { + + // First of all work out how many bits are set + + DWORD SetBits = CountSetBits(pBitFields[iColour]); + if (SetBits > iMAXBITS || SetBits == 0) { + NOTE1("Bit fields for component %d invalid",iColour); + return FALSE; + } + + // Next work out the number of zero bits prefix + DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]); + + // This is going to see if all the bits set are contiguous (as they + // should be). We know how much to shift them right by from the + // count of prefix bits. The number of bits set defines a mask, we + // invert this (ones complement) and AND it with the shifted bit + // fields. If the result is NON zero then there are bit(s) sticking + // out the left hand end which means they are not contiguous + + DWORD TestField = pBitFields[iColour] >> PrefixBits; + DWORD Mask = ULONG_MAX << SetBits; + if (TestField & Mask) { + NOTE1("Bit fields for component %d not contiguous",iColour); + return FALSE; + } + } + return TRUE; +} + + +// This counts the number of bits set in the input field + +DWORD CImageDisplay::CountSetBits(DWORD Field) +{ + // This is a relatively well known bit counting algorithm + + DWORD Count = 0; + DWORD init = Field; + + // Until the input is exhausted, count the number of bits + + while (init) { + init = init & (init - 1); // Turn off the bottommost bit + Count++; + } + return Count; +} + + +// This counts the number of zero bits upto the first one set NOTE the input +// field should have been previously checked to ensure there is at least one +// set although if we don't find one set we return the impossible value 32 + +DWORD CImageDisplay::CountPrefixBits(DWORD Field) +{ + DWORD Mask = 1; + DWORD Count = 0; + + while (TRUE) { + if (Field & Mask) { + return Count; + } + Count++; + + ASSERT(Mask != 0x80000000); + if (Mask == 0x80000000) { + return Count; + } + Mask <<= 1; + } +} + + +// This is called to check the BITMAPINFOHEADER for the input type. There are +// many implicit dependancies between the fields in a header structure which +// if we validate now make for easier manipulation in subsequent handling. We +// also check that the BITMAPINFOHEADER matches it's specification such that +// fields likes the number of planes is one, that it's structure size is set +// correctly and that the bitmap dimensions have not been set as negative + +BOOL CImageDisplay::CheckHeaderValidity(const VIDEOINFO *pInput) +{ + // Check the bitmap width and height are not negative. + + if (pInput->bmiHeader.biWidth <= 0 || + pInput->bmiHeader.biHeight <= 0) { + NOTE("Invalid bitmap dimensions"); + return FALSE; + } + + // Check the compression is either BI_RGB or BI_BITFIELDS + + if (pInput->bmiHeader.biCompression != BI_RGB) { + if (pInput->bmiHeader.biCompression != BI_BITFIELDS) { + NOTE("Invalid compression format"); + return FALSE; + } + } + + // If BI_BITFIELDS compression format check the colour depth + + if (pInput->bmiHeader.biCompression == BI_BITFIELDS) { + if (pInput->bmiHeader.biBitCount != 16) { + if (pInput->bmiHeader.biBitCount != 32) { + NOTE("BI_BITFIELDS not 16/32 bit depth"); + return FALSE; + } + } + } + + // Check the assumptions about the layout of the bit fields + + if (pInput->bmiHeader.biCompression == BI_BITFIELDS) { + if (CheckBitFields(pInput) == FALSE) { + NOTE("Bit fields are not valid"); + return FALSE; + } + } + + // Are the number of planes equal to one + + if (pInput->bmiHeader.biPlanes != 1) { + NOTE("Number of planes not one"); + return FALSE; + } + + // Check the image size is consistent (it can be zero) + + if (pInput->bmiHeader.biSizeImage != GetBitmapSize(&pInput->bmiHeader)) { + if (pInput->bmiHeader.biSizeImage) { + NOTE("Image size incorrectly set"); + return FALSE; + } + } + + // Check the size of the structure + + if (pInput->bmiHeader.biSize != sizeof(BITMAPINFOHEADER)) { + NOTE("Size of BITMAPINFOHEADER wrong"); + return FALSE; + } + return CheckPaletteHeader(pInput); +} + + +// This runs a few simple tests against the palette fields in the input to +// see if it looks vaguely correct. The tests look at the number of palette +// colours present, the number considered important and the biCompression +// field which should always be BI_RGB as no other formats are meaningful + +BOOL CImageDisplay::CheckPaletteHeader(const VIDEOINFO *pInput) +{ + // The checks here are for palettised videos only + + if (PALETTISED(pInput) == FALSE) { + if (pInput->bmiHeader.biClrUsed) { + NOTE("Invalid palette entries"); + return FALSE; + } + return TRUE; + } + + // Compression type of BI_BITFIELDS is meaningless for palette video + + if (pInput->bmiHeader.biCompression != BI_RGB) { + NOTE("Palettised video must be BI_RGB"); + return FALSE; + } + + // Check the number of palette colours is correct + + if (pInput->bmiHeader.biClrUsed > PALETTE_ENTRIES(pInput)) { + NOTE("Too many colours in palette"); + return FALSE; + } + + // The number of important colours shouldn't exceed the number used + + if (pInput->bmiHeader.biClrImportant > pInput->bmiHeader.biClrUsed) { + NOTE("Too many important colours"); + return FALSE; + } + return TRUE; +} + + +// Return the format of the video display + +const VIDEOINFO *CImageDisplay::GetDisplayFormat() +{ + return &m_Display; +} + + +// Return TRUE if the display uses a palette + +BOOL CImageDisplay::IsPalettised() +{ + return PALETTISED(&m_Display); +} + + +// Return the bit depth of the current display setting + +WORD CImageDisplay::GetDisplayDepth() +{ + return m_Display.bmiHeader.biBitCount; +} + + +// Initialise the optional fields in a VIDEOINFO. These are mainly to do with +// the source and destination rectangles and palette information such as the +// number of colours present. It simplifies our code just a little if we don't +// have to keep checking for all the different valid permutations in a header +// every time we want to do anything with it (an example would be creating a +// palette). We set the base class media type before calling this function so +// that the media types between the pins match after a connection is made + +HRESULT CImageDisplay::UpdateFormat(VIDEOINFO *pVideoInfo) +{ + ASSERT(pVideoInfo); + + BITMAPINFOHEADER *pbmi = HEADER(pVideoInfo); + SetRectEmpty(&pVideoInfo->rcSource); + SetRectEmpty(&pVideoInfo->rcTarget); + + // Set the number of colours explicitly + + if (PALETTISED(pVideoInfo)) { + if (pVideoInfo->bmiHeader.biClrUsed == 0) { + pVideoInfo->bmiHeader.biClrUsed = PALETTE_ENTRIES(pVideoInfo); + } + } + + // The number of important colours shouldn't exceed the number used, on + // some displays the number of important colours is not initialised when + // retrieving the display type so we set the colours used correctly + + if (pVideoInfo->bmiHeader.biClrImportant > pVideoInfo->bmiHeader.biClrUsed) { + pVideoInfo->bmiHeader.biClrImportant = PALETTE_ENTRIES(pVideoInfo); + } + + // Change the image size field to be explicit + + if (pVideoInfo->bmiHeader.biSizeImage == 0) { + pVideoInfo->bmiHeader.biSizeImage = GetBitmapSize(&pVideoInfo->bmiHeader); + } + return NOERROR; +} + + +// Lots of video rendering filters want code to check proposed formats are ok +// This checks the VIDEOINFO we are passed as a media type. If the media type +// is a valid media type then we return NOERROR otherwise E_INVALIDARG. Note +// however we only accept formats that can be easily displayed in the display +// so if we are on a 16 bit device we will not accept 24 bit images. The one +// complexity is that most displays draw 8 bit palettised images efficiently +// Also if the input format is less colour bits per pixel then we also accept + +HRESULT CImageDisplay::CheckVideoType(const VIDEOINFO *pInput) +{ + // First of all check the VIDEOINFOHEADER looks correct + + if (CheckHeaderValidity(pInput) == FALSE) { + return E_INVALIDARG; + } + + // Virtually all devices support palettised images efficiently + + if (m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount) { + if (PALETTISED(pInput) == TRUE) { + ASSERT(PALETTISED(&m_Display) == TRUE); + NOTE("(Video) Type connection ACCEPTED"); + return NOERROR; + } + } + + + // Is the display depth greater than the input format + + if (m_Display.bmiHeader.biBitCount > pInput->bmiHeader.biBitCount) { + NOTE("(Video) Mismatch agreed"); + return NOERROR; + } + + // Is the display depth less than the input format + + if (m_Display.bmiHeader.biBitCount < pInput->bmiHeader.biBitCount) { + NOTE("(Video) Format mismatch"); + return E_INVALIDARG; + } + + + // Both input and display formats are either BI_RGB or BI_BITFIELDS + + ASSERT(m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount); + ASSERT(PALETTISED(pInput) == FALSE); + ASSERT(PALETTISED(&m_Display) == FALSE); + + // BI_RGB 16 bit representation is implicitly RGB555, and likewise BI_RGB + // 24 bit representation is RGB888. So we initialise a pointer to the bit + // fields they really mean and check against the display device format + // This is only going to be called when both formats are equal bits pixel + + const DWORD *pInputMask = GetBitMasks(pInput); + const DWORD *pDisplayMask = GetBitMasks((VIDEOINFO *)&m_Display); + + if (pInputMask[iRED] != pDisplayMask[iRED] || + pInputMask[iGREEN] != pDisplayMask[iGREEN] || + pInputMask[iBLUE] != pDisplayMask[iBLUE]) { + + NOTE("(Video) Bit field mismatch"); + return E_INVALIDARG; + } + + NOTE("(Video) Type connection ACCEPTED"); + return NOERROR; +} + + +// Return the bit masks for the true colour VIDEOINFO provided + +const DWORD *CImageDisplay::GetBitMasks(const VIDEOINFO *pVideoInfo) +{ + static const DWORD FailMasks[] = {0,0,0}; + + if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) { + return BITMASKS(pVideoInfo); + } + + ASSERT(pVideoInfo->bmiHeader.biCompression == BI_RGB); + + switch (pVideoInfo->bmiHeader.biBitCount) { + case 16: return bits555; + case 24: return bits888; + case 32: return bits888; + default: return FailMasks; + } +} + + +// Check to see if we can support media type pmtIn as proposed by the output +// pin - We first check that the major media type is video and also identify +// the media sub type. Then we thoroughly check the VIDEOINFO type provided +// As well as the contained VIDEOINFO being correct the major type must be +// video, the subtype a recognised video format and the type GUID correct + +HRESULT CImageDisplay::CheckMediaType(const CMediaType *pmtIn) +{ + // Does this have a VIDEOINFOHEADER format block + + const GUID *pFormatType = pmtIn->FormatType(); + if (*pFormatType != FORMAT_VideoInfo) { + NOTE("Format GUID not a VIDEOINFOHEADER"); + return E_INVALIDARG; + } + ASSERT(pmtIn->Format()); + + // Check the format looks reasonably ok + + ULONG Length = pmtIn->FormatLength(); + if (Length < SIZE_VIDEOHEADER) { + NOTE("Format smaller than a VIDEOHEADER"); + return E_FAIL; + } + + VIDEOINFO *pInput = (VIDEOINFO *) pmtIn->Format(); + + // Check the major type is MEDIATYPE_Video + + const GUID *pMajorType = pmtIn->Type(); + if (*pMajorType != MEDIATYPE_Video) { + NOTE("Major type not MEDIATYPE_Video"); + return E_INVALIDARG; + } + + // Check we can identify the media subtype + + const GUID *pSubType = pmtIn->Subtype(); + if (GetBitCount(pSubType) == USHRT_MAX) { + NOTE("Invalid video media subtype"); + return E_INVALIDARG; + } + return CheckVideoType(pInput); +} + + +// Given a video format described by a VIDEOINFO structure we return the mask +// that is used to obtain the range of acceptable colours for this type, for +// example, the mask for a 24 bit true colour format is 0xFF in all cases. A +// 16 bit 5:6:5 display format uses 0xF8, 0xFC and 0xF8, therefore given any +// RGB triplets we can AND them with these fields to find one that is valid + +BOOL CImageDisplay::GetColourMask(DWORD *pMaskRed, + DWORD *pMaskGreen, + DWORD *pMaskBlue) +{ + CAutoLock cDisplayLock(this); + *pMaskRed = 0xFF; + *pMaskGreen = 0xFF; + *pMaskBlue = 0xFF; + + // If this format is palettised then it doesn't have bit fields + + if (m_Display.bmiHeader.biBitCount < 16) { + return FALSE; + } + + // If this is a 24 bit true colour display then it can handle all the + // possible colour component ranges described by a byte. It is never + // allowed for a 24 bit colour depth image to have BI_BITFIELDS set + + if (m_Display.bmiHeader.biBitCount == 24) { + ASSERT(m_Display.bmiHeader.biCompression == BI_RGB); + return TRUE; + } + + // Calculate the mask based on the format's bit fields + + const DWORD *pBitFields = (DWORD *) GetBitMasks((VIDEOINFO *)&m_Display); + DWORD *pOutputMask[] = { pMaskRed, pMaskGreen, pMaskBlue }; + + // We know from earlier testing that there are no more than iMAXBITS + // bits set in the mask and that they are all contiguous. All that + // therefore remains is to shift them into the correct position + + for (INT iColour = iRED;iColour <= iBLUE;iColour++) { + + // This works out how many bits there are and where they live + + DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]); + DWORD SetBits = CountSetBits(pBitFields[iColour]); + + // The first shift moves the bit field so that it is right justified + // in the DWORD, after which we then shift it back left which then + // puts the leading bit in the bytes most significant bit position + + *(pOutputMask[iColour]) = pBitFields[iColour] >> PrefixBits; + *(pOutputMask[iColour]) <<= (iMAXBITS - SetBits); + } + return TRUE; +} + + +/* Helper to convert to VIDEOINFOHEADER2 +*/ +STDAPI ConvertVideoInfoToVideoInfo2(AM_MEDIA_TYPE *pmt) +{ + ASSERT(pmt->formattype == FORMAT_VideoInfo); + VIDEOINFO *pVideoInfo = (VIDEOINFO *)pmt->pbFormat; + PVOID pvNew = CoTaskMemAlloc(pmt->cbFormat + sizeof(VIDEOINFOHEADER2) - + sizeof(VIDEOINFOHEADER)); + if (pvNew == NULL) { + return E_OUTOFMEMORY; + } + CopyMemory(pvNew, pmt->pbFormat, FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader)); + ZeroMemory((PBYTE)pvNew + FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader), + sizeof(VIDEOINFOHEADER2) - sizeof(VIDEOINFOHEADER)); + CopyMemory((PBYTE)pvNew + FIELD_OFFSET(VIDEOINFOHEADER2, bmiHeader), + pmt->pbFormat + FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader), + pmt->cbFormat - FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader)); + VIDEOINFOHEADER2 *pVideoInfo2 = (VIDEOINFOHEADER2 *)pvNew; + pVideoInfo2->dwPictAspectRatioX = (DWORD)pVideoInfo2->bmiHeader.biWidth; + pVideoInfo2->dwPictAspectRatioY = (DWORD)pVideoInfo2->bmiHeader.biHeight; + pmt->formattype = FORMAT_VideoInfo2; + CoTaskMemFree(pmt->pbFormat); + pmt->pbFormat = (PBYTE)pvNew; + pmt->cbFormat += sizeof(VIDEOINFOHEADER2) - sizeof(VIDEOINFOHEADER); + return S_OK; +} diff --git a/stepmania/src/BaseClasses/winutil.h b/stepmania/src/BaseClasses/winutil.h new file mode 100644 index 0000000000..cb002c9324 --- /dev/null +++ b/stepmania/src/BaseClasses/winutil.h @@ -0,0 +1,414 @@ +//------------------------------------------------------------------------------ +// File: WinUtil.h +// +// Desc: DirectShow base classes - defines generic handler classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// Make sure that you call PrepareWindow to initialise the window after +// the object has been constructed. It is a separate method so that +// derived classes can override useful methods like MessageLoop. Also +// any derived class must call DoneWithWindow in its destructor. If it +// doesn't a message may be retrieved and call a derived class member +// function while a thread is executing the base class destructor code + +#ifndef __WINUTIL__ +#define __WINUTIL__ + +const int DEFWIDTH = 320; // Initial window width +const int DEFHEIGHT = 240; // Initial window height +const int CAPTION = 256; // Maximum length of caption +const int TIMELENGTH = 50; // Maximum length of times +const int PROFILESTR = 128; // Normal profile string +const WORD PALVERSION = 0x300; // GDI palette version +const LONG PALETTE_VERSION = (LONG) 1; // Initial palette version +const COLORREF VIDEO_COLOUR = 0; // Defaults to black background +const HANDLE hMEMORY = (HANDLE) (-1); // Says to open as memory file + +#define WIDTH(x) ((*(x)).right - (*(x)).left) +#define HEIGHT(x) ((*(x)).bottom - (*(x)).top) +#define SHOWSTAGE TEXT("WM_SHOWSTAGE") +#define SHOWSTAGETOP TEXT("WM_SHOWSTAGETOP") +#define REALIZEPALETTE TEXT("WM_REALIZEPALETTE") + +class AM_NOVTABLE CBaseWindow +{ +protected: + + HINSTANCE m_hInstance; // Global module instance handle + HWND m_hwnd; // Handle for our window + HDC m_hdc; // Device context for the window + LONG m_Width; // Client window width + LONG m_Height; // Client window height + BOOL m_bActivated; // Has the window been activated + LPTSTR m_pClassName; // Static string holding class name + DWORD m_ClassStyles; // Passed in to our constructor + DWORD m_WindowStyles; // Likewise the initial window styles + DWORD m_WindowStylesEx; // And the extended window styles + UINT m_ShowStageMessage; // Have the window shown with focus + UINT m_ShowStageTop; // Makes the window WS_EX_TOPMOST + UINT m_RealizePalette; // Makes us realize our new palette + HDC m_MemoryDC; // Used for fast BitBlt operations + HPALETTE m_hPalette; // Handle to any palette we may have + BYTE m_bNoRealize; // Don't realize palette now + BYTE m_bBackground; // Should we realise in background + BYTE m_bRealizing; // already realizing the palette + CCritSec m_WindowLock; // Serialise window object access + BOOL m_bDoGetDC; // Should this window get a DC + bool m_bDoPostToDestroy; // Use PostMessage to destroy + CCritSec m_PaletteLock; // This lock protects m_hPalette. + // It should be held anytime the + // program use the value of m_hPalette. + + // Maps windows message procedure into C++ methods + friend LRESULT CALLBACK WndProc(HWND hwnd, // Window handle + UINT uMsg, // Message ID + WPARAM wParam, // First parameter + LPARAM lParam); // Other parameter + + virtual LRESULT OnPaletteChange(HWND hwnd, UINT Message); + +public: + + CBaseWindow(BOOL bDoGetDC = TRUE, bool bPostToDestroy = false); + +#ifdef DEBUG + virtual ~CBaseWindow(); +#endif + + virtual HRESULT DoneWithWindow(); + virtual HRESULT PrepareWindow(); + virtual HRESULT InactivateWindow(); + virtual HRESULT ActivateWindow(); + virtual BOOL OnSize(LONG Width, LONG Height); + virtual BOOL OnClose(); + virtual RECT GetDefaultRect(); + virtual HRESULT UninitialiseWindow(); + virtual HRESULT InitialiseWindow(HWND hwnd); + + HRESULT CompleteConnect(); + HRESULT DoCreateWindow(); + + HRESULT PerformanceAlignWindow(); + HRESULT DoShowWindow(LONG ShowCmd); + void PaintWindow(BOOL bErase); + void DoSetWindowForeground(BOOL bFocus); + virtual HRESULT SetPalette(HPALETTE hPalette); + + void SetRealize(BOOL bRealize) + { + m_bNoRealize = !bRealize; + } + + // Jump over to the window thread to set the current palette + HRESULT SetPalette(); + void UnsetPalette(void); + virtual HRESULT DoRealisePalette(BOOL bForceBackground = FALSE); + + void LockPaletteLock(); + void UnlockPaletteLock(); + + virtual BOOL PossiblyEatMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { return FALSE; }; + + // Access our window information + + bool WindowExists(); + LONG GetWindowWidth(); + LONG GetWindowHeight(); + HWND GetWindowHWND(); + HDC GetMemoryHDC(); + HDC GetWindowHDC(); + + #ifdef DEBUG + HPALETTE GetPalette(); + #endif // DEBUG + + // This is the window procedure the derived object should override + + virtual LRESULT OnReceiveMessage(HWND hwnd, // Window handle + UINT uMsg, // Message ID + WPARAM wParam, // First parameter + LPARAM lParam); // Other parameter + + // Must be overriden to return class and window styles + + virtual LPTSTR GetClassWindowStyles( + DWORD *pClassStyles, // Class styles + DWORD *pWindowStyles, // Window styles + DWORD *pWindowStylesEx) PURE; // Extended styles +}; + + +// This helper class is entirely subservient to the owning CBaseWindow object +// All this object does is to split out the actual drawing operation from the +// main object (because it was becoming too large). We have a number of entry +// points to set things like the draw device contexts, to implement the actual +// drawing and to set the destination rectangle in the client window. We have +// no critical section locking in this class because we are used exclusively +// by the owning window object which looks after serialising calls into us + +// If you want to use this class make sure you call NotifyAllocator once the +// allocate has been agreed, also call NotifyMediaType with a pointer to a +// NON stack based CMediaType once that has been set (we keep a pointer to +// the original rather than taking a copy). When the palette changes call +// IncrementPaletteVersion (easiest thing to do is to also call this method +// in the SetMediaType method most filters implement). Finally before you +// start rendering anything call SetDrawContext so that we can get the HDCs +// for drawing from the CBaseWindow object we are given during construction + +class CDrawImage +{ +protected: + + CBaseWindow *m_pBaseWindow; // Owning video window object + CRefTime m_StartSample; // Start time for the current sample + CRefTime m_EndSample; // And likewise it's end sample time + HDC m_hdc; // Main window device context + HDC m_MemoryDC; // Offscreen draw device context + RECT m_TargetRect; // Target destination rectangle + RECT m_SourceRect; // Source image rectangle + BOOL m_bStretch; // Do we have to stretch the images + BOOL m_bUsingImageAllocator; // Are the samples shared DIBSECTIONs + CMediaType *m_pMediaType; // Pointer to the current format + int m_perfidRenderTime; // Time taken to render an image + LONG m_PaletteVersion; // Current palette version cookie + + // Draw the video images in the window + + void SlowRender(IMediaSample *pMediaSample); + void FastRender(IMediaSample *pMediaSample); + void DisplaySampleTimes(IMediaSample *pSample); + void UpdateColourTable(HDC hdc,BITMAPINFOHEADER *pbmi); + void SetStretchMode(); + +public: + + // Used to control the image drawing + + CDrawImage(CBaseWindow *pBaseWindow); + BOOL DrawImage(IMediaSample *pMediaSample); + BOOL DrawVideoImageHere(HDC hdc, IMediaSample *pMediaSample, + LPRECT lprcSrc, LPRECT lprcDst); + void SetDrawContext(); + void SetTargetRect(RECT *pTargetRect); + void SetSourceRect(RECT *pSourceRect); + void GetTargetRect(RECT *pTargetRect); + void GetSourceRect(RECT *pSourceRect); + virtual RECT ScaleSourceRect(const RECT *pSource); + + // Handle updating palettes as they change + + LONG GetPaletteVersion(); + void ResetPaletteVersion(); + void IncrementPaletteVersion(); + + // Tell us media types and allocator assignments + + void NotifyAllocator(BOOL bUsingImageAllocator); + void NotifyMediaType(CMediaType *pMediaType); + BOOL UsingImageAllocator(); + + // Called when we are about to draw an image + + void NotifyStartDraw() { + MSR_START(m_perfidRenderTime); + }; + + // Called when we complete an image rendering + + void NotifyEndDraw() { + MSR_STOP(m_perfidRenderTime); + }; +}; + + +// This is the structure used to keep information about each GDI DIB. All the +// samples we create from our allocator will have a DIBSECTION allocated to +// them. When we receive the sample we know we can BitBlt straight to an HDC + +typedef struct tagDIBDATA { + + LONG PaletteVersion; // Current palette version in use + DIBSECTION DibSection; // Details of DIB section allocated + HBITMAP hBitmap; // Handle to bitmap for drawing + HANDLE hMapping; // Handle to shared memory block + BYTE *pBase; // Pointer to base memory address + +} DIBDATA; + + +// This class inherits from CMediaSample and uses all of it's methods but it +// overrides the constructor to initialise itself with the DIBDATA structure +// When we come to render an IMediaSample we will know if we are using our own +// allocator, and if we are, we can cast the IMediaSample to a pointer to one +// of these are retrieve the DIB section information and hence the HBITMAP + +class CImageSample : public CMediaSample +{ +protected: + + DIBDATA m_DibData; // Information about the DIBSECTION + BOOL m_bInit; // Is the DIB information setup + +public: + + // Constructor + + CImageSample(CBaseAllocator *pAllocator, + TCHAR *pName, + HRESULT *phr, + LPBYTE pBuffer, + LONG length); + + // Maintain the DIB/DirectDraw state + + void SetDIBData(DIBDATA *pDibData); + DIBDATA *GetDIBData(); +}; + + +// This is an allocator based on the abstract CBaseAllocator base class that +// allocates sample buffers in shared memory. The number and size of these +// are determined when the output pin calls Prepare on us. The shared memory +// blocks are used in subsequent calls to GDI CreateDIBSection, once that +// has been done the output pin can fill the buffers with data which will +// then be handed to GDI through BitBlt calls and thereby remove one copy + +class CImageAllocator : public CBaseAllocator +{ +protected: + + CBaseFilter *m_pFilter; // Delegate reference counts to + CMediaType *m_pMediaType; // Pointer to the current format + + // Used to create and delete samples + + HRESULT Alloc(); + void Free(); + + // Manage the shared DIBSECTION and DCI/DirectDraw buffers + + HRESULT CreateDIB(LONG InSize,DIBDATA &DibData); + STDMETHODIMP CheckSizes(ALLOCATOR_PROPERTIES *pRequest); + virtual CImageSample *CreateImageSample(LPBYTE pData,LONG Length); + +public: + + // Constructor and destructor + + CImageAllocator(CBaseFilter *pFilter,TCHAR *pName,HRESULT *phr); +#ifdef DEBUG + ~CImageAllocator(); +#endif + + STDMETHODIMP_(ULONG) NonDelegatingAddRef(); + STDMETHODIMP_(ULONG) NonDelegatingRelease(); + void NotifyMediaType(CMediaType *pMediaType); + + // Agree the number of buffers to be used and their size + + STDMETHODIMP SetProperties( + ALLOCATOR_PROPERTIES *pRequest, + ALLOCATOR_PROPERTIES *pActual); +}; + + +// This class is a fairly specialised helper class for image renderers that +// have to create and manage palettes. The CBaseWindow class looks after +// realising palettes once they have been installed. This class can be used +// to create the palette handles from a media format (which must contain a +// VIDEOINFO structure in the format block). We try to make the palette an +// identity palette to maximise performance and also only change palettes +// if actually required to (we compare palette colours before updating). +// All the methods are virtual so that they can be overriden if so required + +class CImagePalette +{ +protected: + + CBaseWindow *m_pBaseWindow; // Window to realise palette in + CBaseFilter *m_pFilter; // Media filter to send events + CDrawImage *m_pDrawImage; // Object who will be drawing + HPALETTE m_hPalette; // The palette handle we own + +public: + + CImagePalette(CBaseFilter *pBaseFilter, + CBaseWindow *pBaseWindow, + CDrawImage *pDrawImage); + +#ifdef DEBUG + virtual ~CImagePalette(); +#endif + + static HPALETTE MakePalette(const VIDEOINFOHEADER *pVideoInfo, LPSTR szDevice); + HRESULT RemovePalette(); + static HRESULT MakeIdentityPalette(PALETTEENTRY *pEntry,INT iColours, LPSTR szDevice); + HRESULT CopyPalette(const CMediaType *pSrc,CMediaType *pDest); + BOOL ShouldUpdate(const VIDEOINFOHEADER *pNewInfo,const VIDEOINFOHEADER *pOldInfo); + HRESULT PreparePalette(const CMediaType *pmtNew,const CMediaType *pmtOld,LPSTR szDevice); + + BOOL DrawVideoImageHere(HDC hdc, IMediaSample *pMediaSample, LPRECT lprcSrc, LPRECT lprcDst) + { + return m_pDrawImage->DrawVideoImageHere(hdc, pMediaSample, lprcSrc,lprcDst); + } +}; + + +// Another helper class really for video based renderers. Most such renderers +// need to know what the display format is to some degree or another. This +// class initialises itself with the display format. The format can be asked +// for through GetDisplayFormat and various other accessor functions. If a +// filter detects a display format change (perhaps it gets a WM_DEVMODECHANGE +// message then it can call RefreshDisplayType to reset that format). Also +// many video renderers will want to check formats as they are proposed by +// source filters. This class provides methods to check formats and only +// accept those video formats that can be efficiently drawn using GDI calls + +class CImageDisplay : public CCritSec +{ +protected: + + // This holds the display format; biSize should not be too big, so we can + // safely use the VIDEOINFO structure + VIDEOINFO m_Display; + + static DWORD CountSetBits(const DWORD Field); + static DWORD CountPrefixBits(const DWORD Field); + static BOOL CheckBitFields(const VIDEOINFO *pInput); + +public: + + // Constructor and destructor + + CImageDisplay(); + + // Used to manage BITMAPINFOHEADERs and the display format + + const VIDEOINFO *GetDisplayFormat(); + HRESULT RefreshDisplayType(LPSTR szDeviceName); + static BOOL CheckHeaderValidity(const VIDEOINFO *pInput); + static BOOL CheckPaletteHeader(const VIDEOINFO *pInput); + BOOL IsPalettised(); + WORD GetDisplayDepth(); + + // Provide simple video format type checking + + HRESULT CheckMediaType(const CMediaType *pmtIn); + HRESULT CheckVideoType(const VIDEOINFO *pInput); + HRESULT UpdateFormat(VIDEOINFO *pVideoInfo); + const DWORD *GetBitMasks(const VIDEOINFO *pVideoInfo); + + BOOL GetColourMask(DWORD *pMaskRed, + DWORD *pMaskGreen, + DWORD *pMaskBlue); +}; + +// Convert a FORMAT_VideoInfo to FORMAT_VideoInfo2 +STDAPI ConvertVideoInfoToVideoInfo2(AM_MEDIA_TYPE *pmt); + +#endif // __WINUTIL__ + diff --git a/stepmania/src/BaseClasses/wxdebug.cpp b/stepmania/src/BaseClasses/wxdebug.cpp new file mode 100644 index 0000000000..fdf01e320b --- /dev/null +++ b/stepmania/src/BaseClasses/wxdebug.cpp @@ -0,0 +1,1310 @@ +//------------------------------------------------------------------------------ +// File: WXDebug.cpp +// +// Desc: DirectShow base classes - implements ActiveX system debugging +// facilities. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#define _WINDLL + +#include +#include +#include + +#ifdef DEBUG +#ifdef UNICODE +#ifndef _UNICODE +#define _UNICODE +#endif // _UNICODE +#endif // UNICODE +#endif // DEBUG + +#include + +#ifdef DEBUG + +// 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 + +/* For every module and executable we store a debugging level for each of + the five categories (eg LOG_ERROR and LOG_TIMING). This makes it easy + to isolate and debug individual modules without seeing everybody elses + spurious debug output. The keys are stored in the registry under the + HKEY_LOCAL_MACHINE\SOFTWARE\Debug\\ key values + NOTE these must be in the same order as their enumeration definition */ + +TCHAR *pKeyNames[] = { + TEXT("TIMING"), // Timing and performance measurements + TEXT("TRACE"), // General step point call tracing + TEXT("MEMORY"), // Memory and object allocation/destruction + TEXT("LOCKING"), // Locking/unlocking of critical sections + TEXT("ERROR"), // Debug error notification + TEXT("CUSTOM1"), + TEXT("CUSTOM2"), + TEXT("CUSTOM3"), + TEXT("CUSTOM4"), + TEXT("CUSTOM5") + }; + +const TCHAR CAutoTrace::_szEntering[] = TEXT("Entering: %s"); +const TCHAR CAutoTrace::_szLeaving[] = TEXT("Leaving: %s"); + +const INT iMAXLEVELS = NUMELMS(pKeyNames); // Maximum debug categories + +HINSTANCE m_hInst; // Module instance handle +TCHAR m_ModuleName[iDEBUGINFO]; // Cut down module name +DWORD m_Levels[iMAXLEVELS]; // Debug level per category +CRITICAL_SECTION m_CSDebug; // Controls access to list +DWORD m_dwNextCookie; // Next active object ID +ObjectDesc *pListHead = NULL; // First active object +DWORD m_dwObjectCount; // Active object count +BOOL m_bInit = FALSE; // Have we been initialised +HANDLE m_hOutput = INVALID_HANDLE_VALUE; // Optional output written here +DWORD dwWaitTimeout = INFINITE; // Default timeout value +DWORD dwTimeOffset; // Time of first DbgLog call +bool g_fUseKASSERT = false; // don't create messagebox +bool g_fDbgInDllEntryPoint = false; +bool g_fAutoRefreshLevels = false; + +const TCHAR *pBaseKey = TEXT("SOFTWARE\\Debug"); +const TCHAR *pGlobalKey = TEXT("GLOBAL"); +static CHAR *pUnknownName = "UNKNOWN"; + +TCHAR *TimeoutName = TEXT("TIMEOUT"); + +/* This sets the instance handle that the debug library uses to find + the module's file name from the Win32 GetModuleFileName function */ + +void WINAPI DbgInitialise(HINSTANCE hInst) { + InitializeCriticalSection(&m_CSDebug); + m_bInit = TRUE; + + m_hInst = hInst; + DbgInitModuleName(); + if(GetProfileInt(m_ModuleName, TEXT("BreakOnLoad"), 0)) + DebugBreak(); + DbgInitModuleSettings(false); + DbgInitGlobalSettings(true); + dwTimeOffset = timeGetTime(); +} + + +/* This is called to clear up any resources the debug library uses - at the + moment we delete our critical section and the object list. The values we + retrieve from the registry are all done during initialisation but we don't + go looking for update notifications while we are running, if the values + are changed then the application has to be restarted to pick them up */ + +void WINAPI DbgTerminate() { + if(m_hOutput != INVALID_HANDLE_VALUE) { + EXECUTE_ASSERT(CloseHandle(m_hOutput)); + m_hOutput = INVALID_HANDLE_VALUE; + } + DeleteCriticalSection(&m_CSDebug); + m_bInit = FALSE; +} + + +/* This is called by DbgInitLogLevels to read the debug settings + for each logging category for this module from the registry */ + +void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax) { + LONG lReturn; // Create key return value + LONG lKeyPos; // Current key category + DWORD dwKeySize; // Size of the key value + DWORD dwKeyType; // Receives it's type + DWORD dwKeyValue; // This fields value + + /* Try and read a value for each key position in turn */ + for(lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) { + + dwKeySize = sizeof(DWORD); + lReturn = RegQueryValueEx(hKey, // Handle to an open key + pKeyNames[lKeyPos], // Subkey name derivation + NULL, // Reserved field + &dwKeyType, // Returns the field type + (LPBYTE) &dwKeyValue, // Returns the field's value + &dwKeySize); // Number of bytes transferred + + /* If either the key was not available or it was not a DWORD value + then we ensure only the high priority debug logging is output + but we try and update the field to a zero filled DWORD value */ + + if(lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) { + + dwKeyValue = 0; + lReturn = RegSetValueEx(hKey, // Handle of an open key + pKeyNames[lKeyPos], // Address of subkey name + (DWORD) 0, // Reserved field + REG_DWORD, // Type of the key field + (PBYTE) &dwKeyValue, // Value for the field + sizeof(DWORD)); // Size of the field buffer + + if(lReturn != ERROR_SUCCESS) { + DbgLog((LOG_ERROR,0,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos])); + dwKeyValue = 0; + } + } + if(fTakeMax) { + m_Levels[lKeyPos] = max(dwKeyValue,m_Levels[lKeyPos]); + } + else { + if((m_Levels[lKeyPos] & LOG_FORCIBLY_SET) == 0) { + m_Levels[lKeyPos] = dwKeyValue; + } + } + } + + /* Read the timeout value for catching hangs */ + dwKeySize = sizeof(DWORD); + lReturn = RegQueryValueEx(hKey, // Handle to an open key + TimeoutName, // Subkey name derivation + NULL, // Reserved field + &dwKeyType, // Returns the field type + (LPBYTE) &dwWaitTimeout, // Returns the field's value + &dwKeySize); // Number of bytes transferred + + /* If either the key was not available or it was not a DWORD value + then we ensure only the high priority debug logging is output + but we try and update the field to a zero filled DWORD value */ + + if(lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) { + + dwWaitTimeout = INFINITE; + lReturn = RegSetValueEx(hKey, // Handle of an open key + TimeoutName, // Address of subkey name + (DWORD) 0, // Reserved field + REG_DWORD, // Type of the key field + (PBYTE) &dwWaitTimeout, // Value for the field + sizeof(DWORD)); // Size of the field buffer + + if(lReturn != ERROR_SUCCESS) { + DbgLog((LOG_ERROR,0,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos])); + dwWaitTimeout = INFINITE; + } + } +} + +void WINAPI DbgOutString(LPCTSTR psz) { + if(m_hOutput != INVALID_HANDLE_VALUE) { + UINT cb = lstrlen(psz); + DWORD dw; +#ifdef UNICODE + CHAR szDest[2048]; + WideCharToMultiByte(CP_ACP, 0, psz, -1, szDest, NUMELMS(szDest), 0, 0); + WriteFile(m_hOutput, szDest, cb, &dw, NULL); +#else + WriteFile(m_hOutput, psz, cb, &dw, NULL); +#endif + } + else { + OutputDebugString(psz); + } +} + +/* Called by DbgInitGlobalSettings to setup alternate logging destinations + */ + +void WINAPI DbgInitLogTo( + HKEY hKey) { + LONG lReturn; + DWORD dwKeyType; + DWORD dwKeySize; + TCHAR szFile[MAX_PATH] = {0}; + static const TCHAR cszKey[] = TEXT("LogToFile"); + + dwKeySize = MAX_PATH; + lReturn = RegQueryValueEx(hKey, // Handle to an open key + cszKey, // Subkey name derivation + NULL, // Reserved field + &dwKeyType, // Returns the field type + (LPBYTE) szFile, // Returns the field's value + &dwKeySize); // Number of bytes transferred + + // create an empty key if it does not already exist + // + if(lReturn != ERROR_SUCCESS || dwKeyType != REG_SZ) { + dwKeySize = sizeof(TCHAR); + lReturn = RegSetValueEx(hKey, // Handle of an open key + cszKey, // Address of subkey name + (DWORD) 0, // Reserved field + REG_SZ, // Type of the key field + (PBYTE)szFile, // Value for the field + dwKeySize); // Size of the field buffer + } + + // if an output-to was specified. try to open it. + // + if(m_hOutput != INVALID_HANDLE_VALUE) { + EXECUTE_ASSERT(CloseHandle(m_hOutput)); + m_hOutput = INVALID_HANDLE_VALUE; + } + if(szFile[0] != 0) { + if(!lstrcmpi(szFile, TEXT("Console"))) { + m_hOutput = GetStdHandle(STD_OUTPUT_HANDLE); + if(m_hOutput == INVALID_HANDLE_VALUE) { + AllocConsole(); + m_hOutput = GetStdHandle(STD_OUTPUT_HANDLE); + } + SetConsoleTitle(TEXT("ActiveX Debug Output")); + } + else if(szFile[0] && + lstrcmpi(szFile, TEXT("Debug")) && + lstrcmpi(szFile, TEXT("Debugger")) && + lstrcmpi(szFile, TEXT("Deb"))) { + m_hOutput = CreateFile(szFile, GENERIC_WRITE, + FILE_SHARE_READ, + NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if(INVALID_HANDLE_VALUE != m_hOutput) { + static const TCHAR cszBar[] = TEXT("\r\n\r\n=====DbgInitialize()=====\r\n\r\n"); + SetFilePointer(m_hOutput, 0, NULL, FILE_END); + DbgOutString(cszBar); + } + } + } +} + + + +/* This is called by DbgInitLogLevels to read the global debug settings for + each logging category for this module from the registry. Normally each + module has it's own values set for it's different debug categories but + setting the global SOFTWARE\Debug\Global applies them to ALL modules */ + +void WINAPI DbgInitGlobalSettings(bool fTakeMax) { + LONG lReturn; // Create key return value + TCHAR szInfo[iDEBUGINFO]; // Constructs key names + HKEY hGlobalKey; // Global override key + + /* Construct the global base key name */ + wsprintf(szInfo,TEXT("%s\\%s"),pBaseKey,pGlobalKey); + + /* Create or open the key for this module */ + lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key + szInfo, // Address of subkey name + (DWORD) 0, // Reserved value + NULL, // Address of class name + (DWORD) 0, // Special options flags + KEY_ALL_ACCESS, // Desired security access + NULL, // Key security descriptor + &hGlobalKey, // Opened handle buffer + NULL); // What really happened + + if(lReturn != ERROR_SUCCESS) { + DbgLog((LOG_ERROR,0,TEXT("Could not access GLOBAL module key"))); + return; + } + + DbgInitKeyLevels(hGlobalKey, fTakeMax); + RegCloseKey(hGlobalKey); +} + + +/* This sets the debugging log levels for the different categories. We start + by opening (or creating if not already available) the SOFTWARE\Debug key + that all these settings live under. We then look at the global values + set under SOFTWARE\Debug\Global which apply on top of the individual + module settings. We then load the individual module registry settings */ + +void WINAPI DbgInitModuleSettings(bool fTakeMax) { + LONG lReturn; // Create key return value + TCHAR szInfo[iDEBUGINFO]; // Constructs key names + HKEY hModuleKey; // Module key handle + + /* Construct the base key name */ + wsprintf(szInfo,TEXT("%s\\%s"),pBaseKey,m_ModuleName); + + /* Create or open the key for this module */ + lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key + szInfo, // Address of subkey name + (DWORD) 0, // Reserved value + NULL, // Address of class name + (DWORD) 0, // Special options flags + KEY_ALL_ACCESS, // Desired security access + NULL, // Key security descriptor + &hModuleKey, // Opened handle buffer + NULL); // What really happened + + if(lReturn != ERROR_SUCCESS) { + DbgLog((LOG_ERROR,0,TEXT("Could not access module key"))); + return; + } + + DbgInitLogTo(hModuleKey); + DbgInitKeyLevels(hModuleKey, fTakeMax); + RegCloseKey(hModuleKey); +} + + +/* Initialise the module file name */ + +void WINAPI DbgInitModuleName() { + TCHAR FullName[iDEBUGINFO]; // Load the full path and module name + TCHAR *pName; // Searches from the end for a backslash + + GetModuleFileName(m_hInst,FullName,iDEBUGINFO); + pName = _tcsrchr(FullName,'\\'); + if(pName == NULL) { + pName = FullName; + } + else { + pName++; + } + lstrcpy(m_ModuleName,pName); +} + +struct MsgBoxMsg { + HWND hwnd; + TCHAR *szTitle; + TCHAR *szMessage; + DWORD dwFlags; + INT iResult; +}; + +// +// create a thread to call MessageBox(). calling MessageBox() on +// random threads at bad times can confuse the host (eg IE). +// +DWORD WINAPI MsgBoxThread( + LPVOID lpParameter // thread data + ) +{ + MsgBoxMsg *pmsg = (MsgBoxMsg *)lpParameter; + pmsg->iResult = MessageBox(pmsg->hwnd, + pmsg->szTitle, + pmsg->szMessage, + pmsg->dwFlags); + + return 0; +} + +INT MessageBoxOtherThread( + HWND hwnd, + TCHAR *szTitle, + TCHAR *szMessage, + DWORD dwFlags) { + if(g_fDbgInDllEntryPoint) { + // can't wait on another thread because we have the loader + // lock held in the dll entry point. + return MessageBox(hwnd, szTitle, szMessage, dwFlags); + } + else { + MsgBoxMsg msg = {hwnd, szTitle, szMessage, dwFlags, 0}; + DWORD dwid; + HANDLE hThread = CreateThread(0, // security + 0, // stack size + MsgBoxThread, + (void *)&msg, // arg + 0, // flags + &dwid); + if(hThread) { + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); + return msg.iResult; + } + + // break into debugger on failure. + return IDCANCEL; + } +} + +/* Displays a message box if the condition evaluated to FALSE */ + +void WINAPI DbgAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine) { + if(g_fUseKASSERT) { + DbgKernelAssert(pCondition, pFileName, iLine); + } + else { + + TCHAR szInfo[iDEBUGINFO]; + + wsprintf(szInfo, TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"), + pCondition, iLine, pFileName); + + INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"), + MB_SYSTEMMODAL | + MB_ICONHAND | + MB_YESNOCANCEL | + MB_SETFOREGROUND); + + switch(MsgId) { + case IDNO: /* Kill the application */ + FatalAppExit(FALSE, TEXT("Application terminated")); + break; + + case IDCANCEL: /* Break into the debugger */ + DebugBreak(); + break; + + case IDYES: /* Ignore assertion continue execution */ + break; + } + } +} + +/* Displays a message box at a break point */ + +void WINAPI DbgBreakPoint(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine) { + if(g_fUseKASSERT) { + DbgKernelAssert(pCondition, pFileName, iLine); + } + else { + TCHAR szInfo[iDEBUGINFO]; + + wsprintf(szInfo, TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"), + pCondition, iLine, pFileName); + + INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"), + MB_SYSTEMMODAL | + MB_ICONHAND | + MB_YESNOCANCEL | + MB_SETFOREGROUND); + + switch(MsgId) { + case IDNO: /* Kill the application */ + FatalAppExit(FALSE, TEXT("Application terminated")); + break; + + case IDCANCEL: /* Break into the debugger */ + DebugBreak(); + break; + + case IDYES: /* Ignore break point continue execution */ + break; + } + } +} + +void WINAPI DbgBreakPoint(const TCHAR *pFileName,INT iLine,const TCHAR* szFormatString,...) { + // A debug break point message can have at most 2000 characters if + // ANSI or UNICODE characters are being used. A debug break point message + // can have between 1000 and 2000 double byte characters in it. If a + // particular message needs more characters, then the value of this constant + // should be increased. + const DWORD MAX_BREAK_POINT_MESSAGE_SIZE = 2000; + + TCHAR szBreakPointMessage[MAX_BREAK_POINT_MESSAGE_SIZE]; + + const DWORD MAX_CHARS_IN_BREAK_POINT_MESSAGE = sizeof(szBreakPointMessage) / sizeof(TCHAR); + + va_list va; + va_start(va, szFormatString); + + int nReturnValue = _vsntprintf(szBreakPointMessage, MAX_CHARS_IN_BREAK_POINT_MESSAGE, szFormatString, va); + + va_end(va); + + // _vsnprintf() returns -1 if an error occurs. + if(-1 == nReturnValue) { + DbgBreak("ERROR in DbgBreakPoint(). The variable length debug message could not be displayed because _vsnprintf() failed."); + return; + } + + ::DbgBreakPoint(szBreakPointMessage, pFileName, iLine); +} + + +/* When we initialised the library we stored in the m_Levels array the current + debug output level for this module for each of the five categories. When + some debug logging is sent to us it can be sent with a combination of the + categories (if it is applicable to many for example) in which case we map + the type's categories into their current debug levels and see if any of + them can be accepted. The function looks at each bit position in turn from + the input type field and then compares it's debug level with the modules. + + A level of 0 means that output is always sent to the debugger. This is + due to producing output if the input level is <= m_Levels. +*/ + + +BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level) { + if(g_fAutoRefreshLevels) { + // re-read the registry every second. We cannot use RegNotify() to + // notice registry changes because it's not available on win9x. + static g_dwLastRefresh = 0; + DWORD dwTime = timeGetTime(); + if(dwTime - g_dwLastRefresh > 1000) { + g_dwLastRefresh = dwTime; + + // there's a race condition: multiple threads could update the + // values. plus read and write not synchronized. no harm + // though. + DbgInitModuleSettings(false); + } + } + + + DWORD Mask = 0x01; + + // If no valid bits are set return FALSE + if((Type & ((1<m_szName = szObjectName; + pObject->m_wszName = wszObjectName; + pObject->m_dwCookie = ++m_dwNextCookie; + pObject->m_pNext = pListHead; + + pListHead = pObject; + m_dwObjectCount++; + + DWORD ObjectCookie = pObject->m_dwCookie; + ASSERT(ObjectCookie); + + if(wszObjectName) { + DbgLog((LOG_MEMORY,2,TEXT("Object created %d (%ls) %d Active"), + pObject->m_dwCookie, wszObjectName, m_dwObjectCount)); + } + else { + DbgLog((LOG_MEMORY,2,TEXT("Object created %d (%hs) %d Active"), + pObject->m_dwCookie, szObjectName, m_dwObjectCount)); + } + + LeaveCriticalSection(&m_CSDebug); + return ObjectCookie; +} + + +/* This is called by the CBaseObject destructor when an object is about to be + destroyed, we are passed the cookie we returned during construction that + identifies this object. We scan the object list for a matching cookie and + remove the object if successful. We also update the active object count */ + +BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie) { + /* Grab the list critical section */ + EnterCriticalSection(&m_CSDebug); + + ObjectDesc *pObject = pListHead; + ObjectDesc *pPrevious = NULL; + + /* Scan the object list looking for a cookie match */ + + while(pObject) { + if(pObject->m_dwCookie == dwCookie) { + break; + } + pPrevious = pObject; + pObject = pObject->m_pNext; + } + + if(pObject == NULL) { + DbgBreak("Apparently destroying a bogus object"); + LeaveCriticalSection(&m_CSDebug); + return FALSE; + } + + /* Is the object at the head of the list */ + + if(pPrevious == NULL) { + pListHead = pObject->m_pNext; + } + else { + pPrevious->m_pNext = pObject->m_pNext; + } + + /* Delete the object and update the housekeeping information */ + + m_dwObjectCount--; + + if(pObject->m_wszName) { + DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%ls) %d Active"), + pObject->m_dwCookie, pObject->m_wszName, m_dwObjectCount)); + } + else { + DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%hs) %d Active"), + pObject->m_dwCookie, pObject->m_szName, m_dwObjectCount)); + } + + delete pObject; + LeaveCriticalSection(&m_CSDebug); + return TRUE; +} + + +/* This runs through the active object list displaying their details */ + +void WINAPI DbgDumpObjectRegister() { + TCHAR szInfo[iDEBUGINFO]; + + /* Grab the list critical section */ + + EnterCriticalSection(&m_CSDebug); + ObjectDesc *pObject = pListHead; + + /* Scan the object list displaying the name and cookie */ + + DbgLog((LOG_MEMORY,2,TEXT(""))); + DbgLog((LOG_MEMORY,2,TEXT(" ID Object Description"))); + DbgLog((LOG_MEMORY,2,TEXT(""))); + + while(pObject) { + if(pObject->m_wszName) { + wsprintf(szInfo,TEXT("%5d (%8x) %30ls"),pObject->m_dwCookie, &pObject, pObject->m_wszName); + } + else { + wsprintf(szInfo,TEXT("%5d (%8x) %30hs"),pObject->m_dwCookie, &pObject, pObject->m_szName); + } + DbgLog((LOG_MEMORY,2,szInfo)); + pObject = pObject->m_pNext; + } + + wsprintf(szInfo,TEXT("Total object count %5d"),m_dwObjectCount); + DbgLog((LOG_MEMORY,2,TEXT(""))); + DbgLog((LOG_MEMORY,1,szInfo)); + LeaveCriticalSection(&m_CSDebug); +} + +/* Debug infinite wait stuff */ +DWORD WINAPI DbgWaitForSingleObject(HANDLE h) { + DWORD dwWaitResult; + do { + dwWaitResult = WaitForSingleObject(h, dwWaitTimeout); + ASSERT(dwWaitResult == WAIT_OBJECT_0); + } while(dwWaitResult == WAIT_TIMEOUT); + return dwWaitResult; +} +DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount, + CONST HANDLE *lpHandles, + BOOL bWaitAll) { + DWORD dwWaitResult; + do { + dwWaitResult = WaitForMultipleObjects(nCount, + lpHandles, + bWaitAll, + dwWaitTimeout); + ASSERT((DWORD)(dwWaitResult - WAIT_OBJECT_0) < MAXIMUM_WAIT_OBJECTS); + } while(dwWaitResult == WAIT_TIMEOUT); + return dwWaitResult; +} + +void WINAPI DbgSetWaitTimeout(DWORD dwTimeout) { + dwWaitTimeout = dwTimeout; +} + +#endif /* DEBUG */ + +#ifdef _OBJBASE_H_ + + /* Stuff for printing out our GUID names */ + + GUID_STRING_ENTRY g_GuidNames[] = { + #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + { #name, { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } }, + #include + }; + + CGuidNameList GuidNames; + int g_cGuidNames = sizeof(g_GuidNames) / sizeof(g_GuidNames[0]); + + char *CGuidNameList::operator [] (const GUID &guid) { + for(int i = 0; i < g_cGuidNames; i++) { + if(g_GuidNames[i].guid == guid) { + return g_GuidNames[i].szName; + } + } + if(guid == GUID_NULL) { + return "GUID_NULL"; + } + + // !!! add something to print FOURCC guids? + + // shouldn't this print the hex CLSID? + return "Unknown GUID Name"; +} + +#endif /* _OBJBASE_H_ */ + +/* CDisp class - display our data types */ + +// clashes with REFERENCE_TIME +CDisp::CDisp(LONGLONG ll, int Format) { + // note: this could be combined with CDisp(LONGLONG) by + // introducing a default format of CDISP_REFTIME + LARGE_INTEGER li; + li.QuadPart = ll; + switch(Format) { + case CDISP_DEC: { + TCHAR temp[20]; + int pos=20; + temp[--pos] = 0; + int digit; + // always output at least one digit + do { + // Get the rightmost digit - we only need the low word + digit = li.LowPart % 10; + li.QuadPart /= 10; + temp[--pos] = (TCHAR) (digit+TEXT('0')); + } while(li.QuadPart); + wsprintf(m_String, TEXT("%s"), temp+pos); + break; + } + case CDISP_HEX: + default: + wsprintf(m_String, TEXT("0x%X%8.8X"), li.HighPart, li.LowPart); + } +}; + +CDisp::CDisp(REFCLSID clsid) { + WCHAR strClass[CHARS_IN_GUID+1]; + StringFromGUID2(clsid, strClass, sizeof(strClass) / sizeof(strClass[0])); + ASSERT(sizeof(m_String)/sizeof(m_String[0]) >= CHARS_IN_GUID+1); + wsprintf(m_String, TEXT("%ls"), strClass); +}; + +#ifdef __STREAMS__ +/* Display stuff */ +CDisp::CDisp(CRefTime llTime) { + LPTSTR lpsz = m_String; + LONGLONG llDiv; + if(llTime < 0) { + llTime = -llTime; + lpsz += wsprintf(lpsz, TEXT("-")); + } + llDiv = (LONGLONG)24 * 3600 * 10000000; + if(llTime >= llDiv) { + lpsz += wsprintf(lpsz, TEXT("%d days "), (LONG)(llTime / llDiv)); + llTime = llTime % llDiv; + } + llDiv = (LONGLONG)3600 * 10000000; + if(llTime >= llDiv) { + lpsz += wsprintf(lpsz, TEXT("%d hrs "), (LONG)(llTime / llDiv)); + llTime = llTime % llDiv; + } + llDiv = (LONGLONG)60 * 10000000; + if(llTime >= llDiv) { + lpsz += wsprintf(lpsz, TEXT("%d mins "), (LONG)(llTime / llDiv)); + llTime = llTime % llDiv; + } + wsprintf(lpsz, TEXT("%d.%3.3d sec"), + (LONG)llTime / 10000000, + (LONG)((llTime % 10000000) / 10000)); +}; + +#endif // __STREAMS__ + + +/* Display pin */ +CDisp::CDisp(IPin *pPin) { + PIN_INFO pi; + TCHAR str[MAX_PIN_NAME]; + CLSID clsid; + + if(pPin) { + pPin->QueryPinInfo(&pi); + pi.pFilter->GetClassID(&clsid); + QueryPinInfoReleaseFilter(pi); +#ifndef UNICODE + WideCharToMultiByte(GetACP(), 0, pi.achName, lstrlenW(pi.achName) + 1, + str, MAX_PIN_NAME, NULL, NULL); +#else + lstrcpy(str, pi.achName); +#endif + } + else { + lstrcpy(str, TEXT("NULL IPin")); + } + + m_pString = (PTCHAR) new TCHAR[lstrlen(str)+64]; + if(!m_pString) { + return; + } + + wsprintf(m_pString, TEXT("%hs(%s)"), GuidNames[clsid], str); +} + +/* Display filter or pin */ +CDisp::CDisp(IUnknown *pUnk) { + IBaseFilter *pf; + HRESULT hr = pUnk->QueryInterface(IID_IBaseFilter, (void **)&pf); + if(SUCCEEDED(hr)) { + FILTER_INFO fi; + hr = pf->QueryFilterInfo(&fi); + if(SUCCEEDED(hr)) { + QueryFilterInfoReleaseGraph(fi); + + m_pString = new TCHAR[lstrlenW(fi.achName) + 1]; + if(m_pString) { + wsprintf(m_pString, TEXT("%ls"), fi.achName); + } + } + + pf->Release(); + + return; + } + + IPin *pp; + hr = pUnk->QueryInterface(IID_IPin, (void **)&pp); + if(SUCCEEDED(hr)) { + CDisp::CDisp(pp); + pp->Release(); + return; + } +} + + +CDisp::~CDisp() { +} + +CDispBasic::~CDispBasic() { + if(m_pString != m_String) { + delete [] m_pString; + } +} + +CDisp::CDisp(double d) { +#ifdef DEBUG + _stprintf(m_String, TEXT("%.16g"), d); +#else + wsprintf(m_String, TEXT("%d.%03d"), (int) d, (int) ((d - (int) d) * 1000)); +#endif +} + + +/* If built for debug this will display the media type details. We convert the + major and subtypes into strings and also ask the base classes for a string + description of the subtype, so MEDIASUBTYPE_RGB565 becomes RGB 565 16 bit + We also display the fields in the BITMAPINFOHEADER structure, this should + succeed as we do not accept input types unless the format is big enough */ + +#ifdef DEBUG +void WINAPI DisplayType(LPTSTR label, const AM_MEDIA_TYPE *pmtIn) { + + /* Dump the GUID types and a short description */ + + DbgLog((LOG_TRACE,5,TEXT(""))); + DbgLog((LOG_TRACE,2,TEXT("%s M type %s S type %s"), label, + GuidNames[pmtIn->majortype], + GuidNames[pmtIn->subtype])); + DbgLog((LOG_TRACE,5,TEXT("Subtype description %s"),GetSubtypeName(&pmtIn->subtype))); + + /* Dump the generic media types */ + + if(pmtIn->bTemporalCompression) { + DbgLog((LOG_TRACE,5,TEXT("Temporally compressed"))); + } + else { + DbgLog((LOG_TRACE,5,TEXT("Not temporally compressed"))); + } + + if(pmtIn->bFixedSizeSamples) { + DbgLog((LOG_TRACE,5,TEXT("Sample size %d"),pmtIn->lSampleSize)); + } + else { + DbgLog((LOG_TRACE,5,TEXT("Variable size samples"))); + } + + if(pmtIn->formattype == FORMAT_VideoInfo) { + /* Dump the contents of the BITMAPINFOHEADER structure */ + BITMAPINFOHEADER *pbmi = HEADER(pmtIn->pbFormat); + VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *)pmtIn->pbFormat; + + DbgLog((LOG_TRACE,5,TEXT("Source rectangle (Left %d Top %d Right %d Bottom %d)"), + pVideoInfo->rcSource.left, + pVideoInfo->rcSource.top, + pVideoInfo->rcSource.right, + pVideoInfo->rcSource.bottom)); + + DbgLog((LOG_TRACE,5,TEXT("Target rectangle (Left %d Top %d Right %d Bottom %d)"), + pVideoInfo->rcTarget.left, + pVideoInfo->rcTarget.top, + pVideoInfo->rcTarget.right, + pVideoInfo->rcTarget.bottom)); + + DbgLog((LOG_TRACE,5,TEXT("Size of BITMAPINFO structure %d"),pbmi->biSize)); + if(pbmi->biCompression < 256) { + DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit (%d)"), + pbmi->biWidth, pbmi->biHeight, + pbmi->biBitCount, pbmi->biCompression)); + } + else { + DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit '%4.4hs'"), + pbmi->biWidth, pbmi->biHeight, + pbmi->biBitCount, &pbmi->biCompression)); + } + + DbgLog((LOG_TRACE,2,TEXT("Image size %d"),pbmi->biSizeImage)); + DbgLog((LOG_TRACE,5,TEXT("Planes %d"),pbmi->biPlanes)); + DbgLog((LOG_TRACE,5,TEXT("X Pels per metre %d"),pbmi->biXPelsPerMeter)); + DbgLog((LOG_TRACE,5,TEXT("Y Pels per metre %d"),pbmi->biYPelsPerMeter)); + DbgLog((LOG_TRACE,5,TEXT("Colours used %d"),pbmi->biClrUsed)); + + } + else if(pmtIn->majortype == MEDIATYPE_Audio) { + DbgLog((LOG_TRACE,2,TEXT(" Format type %hs"), + GuidNames[pmtIn->formattype])); + DbgLog((LOG_TRACE,2,TEXT(" Subtype %hs"), + GuidNames[pmtIn->subtype])); + + if((pmtIn->subtype != MEDIASUBTYPE_MPEG1Packet) + && (pmtIn->cbFormat >= sizeof(PCMWAVEFORMAT))) { + /* Dump the contents of the WAVEFORMATEX type-specific format structure */ + + WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmtIn->pbFormat; + DbgLog((LOG_TRACE,2,TEXT("wFormatTag %u"), pwfx->wFormatTag)); + DbgLog((LOG_TRACE,2,TEXT("nChannels %u"), pwfx->nChannels)); + DbgLog((LOG_TRACE,2,TEXT("nSamplesPerSec %lu"), pwfx->nSamplesPerSec)); + DbgLog((LOG_TRACE,2,TEXT("nAvgBytesPerSec %lu"), pwfx->nAvgBytesPerSec)); + DbgLog((LOG_TRACE,2,TEXT("nBlockAlign %u"), pwfx->nBlockAlign)); + DbgLog((LOG_TRACE,2,TEXT("wBitsPerSample %u"), pwfx->wBitsPerSample)); + + /* PCM uses a WAVEFORMAT and does not have the extra size field */ + + if(pmtIn->cbFormat >= sizeof(WAVEFORMATEX)) { + DbgLog((LOG_TRACE,2,TEXT("cbSize %u"), pwfx->cbSize)); + } + } + else { + } + + } + else { + DbgLog((LOG_TRACE,2,TEXT(" Format type %hs"), + GuidNames[pmtIn->formattype])); + // !!!! should add code to dump wave format, others + } +} + + +void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel) { + if(!pGraph) { + return; + } + + IEnumFilters *pFilters; + + DbgLog((LOG_TRACE,dwLevel,TEXT("DumpGraph [%x]"), pGraph)); + + if(FAILED(pGraph->EnumFilters(&pFilters))) { + DbgLog((LOG_TRACE,dwLevel,TEXT("EnumFilters failed!"))); + } + + IBaseFilter *pFilter; + ULONG n; + while(pFilters->Next(1, &pFilter, &n) == S_OK) { + FILTER_INFO info; + + if(FAILED(pFilter->QueryFilterInfo(&info))) { + DbgLog((LOG_TRACE,dwLevel,TEXT(" Filter [%x] -- failed QueryFilterInfo"), pFilter)); + } + else { + QueryFilterInfoReleaseGraph(info); + + // !!! should QueryVendorInfo here! + + DbgLog((LOG_TRACE,dwLevel,TEXT(" Filter [%x] '%ls'"), pFilter, info.achName)); + + IEnumPins *pins; + + if(FAILED(pFilter->EnumPins(&pins))) { + DbgLog((LOG_TRACE,dwLevel,TEXT("EnumPins failed!"))); + } + else { + + IPin *pPin; + while(pins->Next(1, &pPin, &n) == S_OK) { + PIN_INFO info; + + if(FAILED(pPin->QueryPinInfo(&info))) { + DbgLog((LOG_TRACE,dwLevel,TEXT(" Pin [%x] -- failed QueryPinInfo"), pPin)); + } + else { + QueryPinInfoReleaseFilter(info); + + IPin *pPinConnected = NULL; + + HRESULT hr = pPin->ConnectedTo(&pPinConnected); + + if(pPinConnected) { + DbgLog((LOG_TRACE,dwLevel,TEXT(" Pin [%x] '%ls' [%sput]") + TEXT(" Connected to pin [%x]"), + pPin, info.achName, + info.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out"), + pPinConnected)); + + pPinConnected->Release(); + + // perhaps we should really dump the type both ways as a sanity + // check? + if(info.dir == PINDIR_OUTPUT) { + AM_MEDIA_TYPE mt; + + hr = pPin->ConnectionMediaType(&mt); + + if(SUCCEEDED(hr)) { + DisplayType(TEXT("Connection type"), &mt); + + FreeMediaType(mt); + } + } + } + else { + DbgLog((LOG_TRACE,dwLevel, + TEXT(" Pin [%x] '%ls' [%sput]"), + pPin, info.achName, + info.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out"))); + + } + } + + pPin->Release(); + + } + + pins->Release(); + } + + } + + pFilter->Release(); + } + + pFilters->Release(); + +} + +#endif + + diff --git a/stepmania/src/BaseClasses/wxdebug.h b/stepmania/src/BaseClasses/wxdebug.h new file mode 100644 index 0000000000..4addf00c74 --- /dev/null +++ b/stepmania/src/BaseClasses/wxdebug.h @@ -0,0 +1,379 @@ +//------------------------------------------------------------------------------ +// File: WXDebug.h +// +// Desc: DirectShow base classes - provides debugging facilities. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __WXDEBUG__ +#define __WXDEBUG__ + +// This library provides fairly straight forward debugging functionality, this +// is split into two main sections. The first is assertion handling, there are +// three types of assertions provided here. The most commonly used one is the +// ASSERT(condition) macro which will pop up a message box including the file +// and line number if the condition evaluates to FALSE. Then there is the +// EXECUTE_ASSERT macro which is the same as ASSERT except the condition will +// still be executed in NON debug builds. The final type of assertion is the +// KASSERT macro which is more suitable for pure (perhaps kernel) filters as +// the condition is printed onto the debugger rather than in a message box. +// +// The other part of the debug module facilties is general purpose logging. +// This is accessed by calling DbgLog(). The function takes a type and level +// field which define the type of informational string you are presenting and +// it's relative importance. The type field can be a combination (one or more) +// of LOG_TIMING, LOG_TRACE, LOG_MEMORY, LOG_LOCKING and LOG_ERROR. The level +// is a DWORD value where zero defines highest important. Use of zero as the +// debug logging level is to be encouraged ONLY for major errors or events as +// they will ALWAYS be displayed on the debugger. Other debug output has it's +// level matched against the current debug output level stored in the registry +// for this module and if less than the current setting it will be displayed. +// +// Each module or executable has it's own debug output level for each of the +// five types. These are read in when the DbgInitialise function is called +// for DLLs linking to STRMBASE.LIB this is done automatically when the DLL +// is loaded, executables must call it explicitely with the module instance +// handle given to them through the WINMAIN entry point. An executable must +// also call DbgTerminate when they have finished to clean up the resources +// the debug library uses, once again this is done automatically for DLLs + +// These are the five different categories of logging information + +enum { LOG_TIMING = 0x01, // Timing and performance measurements + LOG_TRACE = 0x02, // General step point call tracing + LOG_MEMORY = 0x04, // Memory and object allocation/destruction + LOG_LOCKING = 0x08, // Locking/unlocking of critical sections + LOG_ERROR = 0x10, // Debug error notification + LOG_CUSTOM1 = 0x20, + LOG_CUSTOM2 = 0x40, + LOG_CUSTOM3 = 0x80, + LOG_CUSTOM4 = 0x100, + LOG_CUSTOM5 = 0x200, +}; + +#define LOG_FORCIBLY_SET 0x80000000 + +enum { CDISP_HEX = 0x01, + CDISP_DEC = 0x02}; + +// For each object created derived from CBaseObject (in debug builds) we +// create a descriptor that holds it's name (statically allocated memory) +// and a cookie we assign it. We keep a list of all the active objects +// we have registered so that we can dump a list of remaining objects + +typedef struct tag_ObjectDesc { + const CHAR *m_szName; + const WCHAR *m_wszName; + DWORD m_dwCookie; + tag_ObjectDesc *m_pNext; +} ObjectDesc; + +#define DLLIMPORT __declspec(dllimport) +#define DLLEXPORT __declspec(dllexport) + +#ifdef DEBUG + + #define NAME(x) TEXT(x) + + // These are used internally by the debug library (PRIVATE) + + void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax); + void WINAPI DbgInitGlobalSettings(bool fTakeMax); + void WINAPI DbgInitModuleSettings(bool fTakeMax); + void WINAPI DbgInitModuleName(); + DWORD WINAPI DbgRegisterObjectCreation( + const CHAR *szObjectName, const WCHAR *wszObjectName); + + BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie); + + // These are the PUBLIC entry points + + BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level); + void WINAPI DbgSetModuleLevel(DWORD Type,DWORD Level); + void WINAPI DbgSetAutoRefreshLevels(bool fAuto); + + // Initialise the library with the module handle + + void WINAPI DbgInitialise(HINSTANCE hInst); + void WINAPI DbgTerminate(); + + void WINAPI DbgDumpObjectRegister(); + + // Display error and logging to the user + + void WINAPI DbgAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine); + void WINAPI DbgBreakPoint(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine); + void WINAPI DbgBreakPoint(const TCHAR *pFileName,INT iLine,const TCHAR* szFormatString,...); + + void WINAPI DbgKernelAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine); + void WINAPI DbgLogInfo(DWORD Type,DWORD Level,const TCHAR *pFormat,...); +#ifdef UNICODE + void WINAPI DbgLogInfo(DWORD Type,DWORD Level,const CHAR *pFormat,...); + void WINAPI DbgAssert(const CHAR *pCondition,const CHAR *pFileName,INT iLine); + void WINAPI DbgBreakPoint(const CHAR *pCondition,const CHAR *pFileName,INT iLine); + void WINAPI DbgKernelAssert(const CHAR *pCondition,const CHAR *pFileName,INT iLine); +#endif + void WINAPI DbgOutString(LPCTSTR psz); + + // Debug infinite wait stuff + DWORD WINAPI DbgWaitForSingleObject(HANDLE h); + DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount, + CONST HANDLE *lpHandles, + BOOL bWaitAll); + void WINAPI DbgSetWaitTimeout(DWORD dwTimeout); + +#ifdef __strmif_h__ + // Display a media type: Terse at level 2, verbose at level 5 + void WINAPI DisplayType(LPTSTR label, const AM_MEDIA_TYPE *pmtIn); + + // Dump lots of information about a filter graph + void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel); +#endif + + #define KASSERT(_x_) if (!(_x_)) \ + DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) + + // Break on the debugger without putting up a message box + // message goes to debugger instead + + #define KDbgBreak(_x_) \ + DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) + + // We chose a common name for our ASSERT macro, MFC also uses this name + // So long as the implementation evaluates the condition and handles it + // then we will be ok. Rather than override the behaviour expected we + // will leave whatever first defines ASSERT as the handler (i.e. MFC) + #ifndef ASSERT + #define ASSERT(_x_) if (!(_x_)) \ + DbgAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) + #endif + + #define DbgAssertAligned( _ptr_, _alignment_ ) ASSERT( ((DWORD_PTR) (_ptr_)) % (_alignment_) == 0) + + // Put up a message box informing the user of a halt + // condition in the program + + #define DbgBreak(_x_) \ + DbgBreakPoint(TEXT(#_x_),TEXT(__FILE__),__LINE__) + + #define EXECUTE_ASSERT(_x_) ASSERT(_x_) + #define DbgLog(_x_) DbgLogInfo _x_ + // MFC style trace macros + + #define NOTE(_x_) DbgLog((LOG_TRACE,5,TEXT(_x_))) + #define NOTE1(_x_,a) DbgLog((LOG_TRACE,5,TEXT(_x_),a)) + #define NOTE2(_x_,a,b) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b)) + #define NOTE3(_x_,a,b,c) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c)) + #define NOTE4(_x_,a,b,c,d) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d)) + #define NOTE5(_x_,a,b,c,d,e) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d,e)) + +#else + + // Retail builds make public debug functions inert - WARNING the source + // files do not define or build any of the entry points in debug builds + // (public entry points compile to nothing) so if you go trying to call + // any of the private entry points in your source they won't compile + + #define NAME(_x_) ((TCHAR *) NULL) + + #define DbgInitialise(hInst) + #define DbgTerminate() + #define DbgLog(_x_) 0 + #define DbgOutString(psz) + #define DbgAssertAligned( _ptr_, _alignment_ ) 0 + + #define DbgRegisterObjectCreation(pObjectName) + #define DbgRegisterObjectDestruction(dwCookie) + #define DbgDumpObjectRegister() + + #define DbgCheckModuleLevel(Type,Level) + #define DbgSetModuleLevel(Type,Level) + #define DbgSetAutoRefreshLevels(fAuto) + + #define DbgWaitForSingleObject(h) WaitForSingleObject(h, INFINITE) + #define DbgWaitForMultipleObjects(nCount, lpHandles, bWaitAll) \ + WaitForMultipleObjects(nCount, lpHandles, bWaitAll, INFINITE) + #define DbgSetWaitTimeout(dwTimeout) + + #define KDbgBreak(_x_) + #define DbgBreak(_x_) + + #define KASSERT(_x_) ((void)0) + #ifndef ASSERT + #define ASSERT(_x_) ((void)0) + #endif + #define EXECUTE_ASSERT(_x_) ((void)(_x_)) + + // MFC style trace macros + + #define NOTE(_x_) ((void)0) + #define NOTE1(_x_,a) ((void)0) + #define NOTE2(_x_,a,b) ((void)0) + #define NOTE3(_x_,a,b,c) ((void)0) + #define NOTE4(_x_,a,b,c,d) ((void)0) + #define NOTE5(_x_,a,b,c,d,e) ((void)0) + + #define DisplayType(label, pmtIn) ((void)0) + #define DumpGraph(pGraph, label) ((void)0) +#endif + + +// Checks a pointer which should be non NULL - can be used as follows. + +#define CheckPointer(p,ret) {if((p)==NULL) return (ret);} + +// HRESULT Foo(VOID *pBar) +// { +// CheckPointer(pBar,E_INVALIDARG) +// } +// +// Or if the function returns a boolean +// +// BOOL Foo(VOID *pBar) +// { +// CheckPointer(pBar,FALSE) +// } + +// These validate pointers when symbol VFWROBUST is defined +// This will normally be defined in debug not retail builds + +#ifdef DEBUG + #define VFWROBUST +#endif + +#ifdef VFWROBUST + + #define ValidateReadPtr(p,cb) \ + {if(IsBadReadPtr((PVOID)p,cb) == TRUE) \ + DbgBreak("Invalid read pointer");} + + #define ValidateWritePtr(p,cb) \ + {if(IsBadWritePtr((PVOID)p,cb) == TRUE) \ + DbgBreak("Invalid write pointer");} + + #define ValidateReadWritePtr(p,cb) \ + {ValidateReadPtr(p,cb) ValidateWritePtr(p,cb)} + + #define ValidateStringPtr(p) \ + {if(IsBadStringPtr((LPCTSTR)p,INFINITE) == TRUE) \ + DbgBreak("Invalid string pointer");} + + #define ValidateStringPtrA(p) \ + {if(IsBadStringPtrA((LPCSTR)p,INFINITE) == TRUE) \ + DbgBreak("Invalid ANSI string pointer");} + + #define ValidateStringPtrW(p) \ + {if(IsBadStringPtrW((LPCWSTR)p,INFINITE) == TRUE) \ + DbgBreak("Invalid UNICODE string pointer");} + +#else + #define ValidateReadPtr(p,cb) 0 + #define ValidateWritePtr(p,cb) 0 + #define ValidateReadWritePtr(p,cb) 0 + #define ValidateStringPtr(p) 0 + #define ValidateStringPtrA(p) 0 + #define ValidateStringPtrW(p) 0 +#endif + + +#ifdef _OBJBASE_H_ + + // Outputting GUID names. If you want to include the name + // associated with a GUID (eg CLSID_...) then + // + // GuidNames[yourGUID] + // + // Returns the name defined in uuids.h as a string + + typedef struct { + CHAR *szName; + GUID guid; + } GUID_STRING_ENTRY; + + class CGuidNameList { + public: + CHAR *operator [] (const GUID& guid); + }; + + extern CGuidNameList GuidNames; + +#endif + +#ifndef REMIND + // REMIND macro - generates warning as reminder to complete coding + // (eg) usage: + // + // #pragma message (REMIND("Add automation support")) + + + #define QUOTE(x) #x + #define QQUOTE(y) QUOTE(y) + #define REMIND(str) __FILE__ "(" QQUOTE(__LINE__) ") : " str +#endif + +// Method to display objects in a useful format +// +// eg If you want to display a LONGLONG ll in a debug string do (eg) +// +// DbgLog((LOG_TRACE, n, TEXT("Value is %s"), (LPCTSTR)CDisp(ll, CDISP_HEX))); + + +class CDispBasic +{ +public: + CDispBasic() { m_pString = m_String; }; + ~CDispBasic(); +protected: + PTCHAR m_pString; // normally points to m_String... unless too much data + TCHAR m_String[50]; +}; +class CDisp : public CDispBasic +{ +public: + CDisp(LONGLONG ll, int Format = CDISP_HEX); // Display a LONGLONG in CDISP_HEX or CDISP_DEC form + CDisp(REFCLSID clsid); // Display a GUID + CDisp(double d); // Display a floating point number +#ifdef __strmif_h__ +#ifdef __STREAMS__ + CDisp(CRefTime t); // Display a Reference Time +#endif + CDisp(IPin *pPin); // Display a pin as {filter clsid}(pin name) + CDisp(IUnknown *pUnk); // Display a filter or pin +#endif // __strmif_h__ + ~CDisp(); + + // Implement cast to (LPCTSTR) as parameter to logger + operator LPCTSTR() + { + return (LPCTSTR)m_pString; + }; +}; + + +#if defined(DEBUG) +class CAutoTrace +{ +private: + const TCHAR* _szBlkName; + const int _level; + static const TCHAR _szEntering[]; + static const TCHAR _szLeaving[]; +public: + CAutoTrace(const TCHAR* szBlkName, const int level = 15) + : _szBlkName(szBlkName), _level(level) + {DbgLog((LOG_TRACE, _level, _szEntering, _szBlkName));} + + ~CAutoTrace() + {DbgLog((LOG_TRACE, _level, _szLeaving, _szBlkName));} +}; + +#define AMTRACE(_x_) CAutoTrace __trace _x_ +#else +#define AMTRACE(_x_) +#endif + +#endif // __WXDEBUG__ + + diff --git a/stepmania/src/BaseClasses/wxlist.cpp b/stepmania/src/BaseClasses/wxlist.cpp new file mode 100644 index 0000000000..214d096969 --- /dev/null +++ b/stepmania/src/BaseClasses/wxlist.cpp @@ -0,0 +1,885 @@ +//------------------------------------------------------------------------------ +// File: WXList.cpp +// +// Desc: DirectShow base classes - implements a non-MFC based generic list +// template class. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +/* A generic list of pointers to objects. + Objectives: avoid using MFC libraries in ndm kernel mode and + provide a really useful list type. + + The class is thread safe in that separate threads may add and + delete items in the list concurrently although the application + must ensure that constructor and destructor access is suitably + synchronised. + + The list name must not conflict with MFC classes as an + application may use both + + The nodes form a doubly linked, NULL terminated chain with an anchor + block (the list object per se) holding pointers to the first and last + nodes and a count of the nodes. + There is a node cache to reduce the allocation and freeing overhead. + It optionally (determined at construction time) has an Event which is + set whenever the list becomes non-empty and reset whenever it becomes + empty. + It optionally (determined at construction time) has a Critical Section + which is entered during the important part of each operation. (About + all you can do outside it is some parameter checking). + + The node cache is a repository of nodes that are NOT in the list to speed + up storage allocation. Each list has its own cache to reduce locking and + serialising. The list accesses are serialised anyway for a given list - a + common cache would mean that we would have to separately serialise access + of all lists within the cache. Because the cache only stores nodes that are + not in the list, releasing the cache does not release any list nodes. This + means that list nodes can be copied or rechained from one list to another + without danger of creating a dangling reference if the original cache goes + away. + + Questionable design decisions: + 1. Retaining the warts for compatibility + 2. Keeping an element count -i.e. counting whenever we do anything + instead of only when we want the count. + 3. Making the chain pointers NULL terminated. If the list object + itself looks just like a node and the list is kept as a ring then + it reduces the number of special cases. All inserts look the same. +*/ + + +#include + +/* set cursor to the position of each element of list in turn */ +#define INTERNALTRAVERSELIST(list, cursor) \ +for ( cursor = (list).GetHeadPositionI() \ + ; cursor!=NULL \ + ; cursor = (list).Next(cursor) \ + ) + + +/* set cursor to the position of each element of list in turn + in reverse order +*/ +#define INTERNALREVERSETRAVERSELIST(list, cursor) \ +for ( cursor = (list).GetTailPositionI() \ + ; cursor!=NULL \ + ; cursor = (list).Prev(cursor) \ + ) + +/* Constructor calls a separate initialisation function that + creates a node cache, optionally creates a lock object + and optionally creates a signaling object. + + By default we create a locking object, a DEFAULTCACHE sized + cache but no event object so the list cannot be used in calls + to WaitForSingleObject +*/ +CBaseList::CBaseList(TCHAR *pName, // Descriptive list name + INT iItems) : // Node cache size +#ifdef DEBUG + CBaseObject(pName), +#endif + m_pFirst(NULL), + m_pLast(NULL), + m_Count(0), + m_Cache(iItems) +{ +} // constructor + +CBaseList::CBaseList(TCHAR *pName) : // Descriptive list name +#ifdef DEBUG + CBaseObject(pName), +#endif + m_pFirst(NULL), + m_pLast(NULL), + m_Count(0), + m_Cache(DEFAULTCACHE) +{ +} // constructor + +#ifdef UNICODE +CBaseList::CBaseList(CHAR *pName, // Descriptive list name + INT iItems) : // Node cache size +#ifdef DEBUG + CBaseObject(pName), +#endif + m_pFirst(NULL), + m_pLast(NULL), + m_Count(0), + m_Cache(iItems) +{ +} // constructor + +CBaseList::CBaseList(CHAR *pName) : // Descriptive list name +#ifdef DEBUG + CBaseObject(pName), +#endif + m_pFirst(NULL), + m_pLast(NULL), + m_Count(0), + m_Cache(DEFAULTCACHE) +{ +} // constructor + +#endif + +/* The destructor enumerates all the node objects in the list and + in the cache deleting each in turn. We do not do any processing + on the objects that the list holds (i.e. points to) so if they + represent interfaces for example the creator of the list should + ensure that each of them is released before deleting us +*/ +CBaseList::~CBaseList() +{ + /* Delete all our list nodes */ + + RemoveAll(); + +} // destructor + +/* Remove all the nodes from the list but don't do anything + with the objects that each node looks after (this is the + responsibility of the creator). + Aa a last act we reset the signalling event + (if available) to indicate to clients that the list + does not have any entries in it. +*/ +void CBaseList::RemoveAll() +{ + /* Free up all the CNode objects NOTE we don't bother putting the + deleted nodes into the cache as this method is only really called + in serious times of change such as when we are being deleted at + which point the cache will be deleted anway */ + + CNode *pn = m_pFirst; + while (pn) { + CNode *op = pn; + pn = pn->Next(); + delete op; + } + + /* Reset the object count and the list pointers */ + + m_Count = 0; + m_pFirst = m_pLast = NULL; + +} // RemoveAll + + + +/* Return a position enumerator for the entire list. + A position enumerator is a pointer to a node object cast to a + transparent type so all we do is return the head/tail node + pointer in the list. + WARNING because the position is a pointer to a node there is + an implicit assumption for users a the list class that after + deleting an object from the list that any other position + enumerators that you have may be invalid (since the node + may be gone). +*/ +POSITION CBaseList::GetHeadPositionI() const +{ + return (POSITION) m_pFirst; +} // GetHeadPosition + + + +POSITION CBaseList::GetTailPositionI() const +{ + return (POSITION) m_pLast; +} // GetTailPosition + + + +/* Get the number of objects in the list, + Get the lock before accessing the count. + Locking may not be entirely necessary but it has the side effect + of making sure that all operations are complete before we get it. + So for example if a list is being added to this list then that + will have completed in full before we continue rather than seeing + an intermediate albeit valid state +*/ +int CBaseList::GetCountI() const +{ + return m_Count; +} // GetCount + + + +/* Return the object at rp, update rp to the next object from + the list or NULL if you have moved over the last object. + You may still call this function once we return NULL but + we will continue to return a NULL position value +*/ +void *CBaseList::GetNextI(POSITION& rp) const +{ + /* have we reached the end of the list */ + + if (rp == NULL) { + return NULL; + } + + /* Lock the object before continuing */ + + void *pObject; + + /* Copy the original position then step on */ + + CNode *pn = (CNode *) rp; + ASSERT(pn != NULL); + rp = (POSITION) pn->Next(); + + /* Get the object at the original position from the list */ + + pObject = pn->GetData(); + // ASSERT(pObject != NULL); // NULL pointers in the list are allowed. + return pObject; +} //GetNext + + + +/* Return the object at p. + Asking for the object at NULL ASSERTs then returns NULL + The object is NOT locked. The list is not being changed + in any way. If another thread is busy deleting the object + then locking would only result in a change from one bad + behaviour to another. +*/ +void *CBaseList::GetI(POSITION p) const +{ + if (p == NULL) { + return NULL; + } + + CNode * pn = (CNode *) p; + void *pObject = pn->GetData(); + // ASSERT(pObject != NULL); // NULL pointers in the list are allowed. + return pObject; +} //Get + + + +/* Return the first position in the list which holds the given pointer. + Return NULL if it's not found. +*/ +POSITION CBaseList::FindI( void * pObj) const +{ + POSITION pn; + INTERNALTRAVERSELIST(*this, pn){ + if (GetI(pn)==pObj) { + return pn; + } + } + return NULL; +} // Find + + + +/* Remove the first node in the list (deletes the pointer to its object + from the list, does not free the object itself). + Return the pointer to its object or NULL if empty +*/ +void *CBaseList::RemoveHeadI() +{ + /* All we do is get the head position and ask for that to be deleted. + We could special case this since some of the code path checking + in Remove() is redundant as we know there is no previous + node for example but it seems to gain little over the + added complexity + */ + + return RemoveI((POSITION)m_pFirst); +} // RemoveHead + + + +/* Remove the last node in the list (deletes the pointer to its object + from the list, does not free the object itself). + Return the pointer to its object or NULL if empty +*/ +void *CBaseList::RemoveTailI() +{ + /* All we do is get the tail position and ask for that to be deleted. + We could special case this since some of the code path checking + in Remove() is redundant as we know there is no previous + node for example but it seems to gain little over the + added complexity + */ + + return RemoveI((POSITION)m_pLast); +} // RemoveTail + + + +/* Remove the pointer to the object in this position from the list. + Deal with all the chain pointers + Return a pointer to the object removed from the list. + The node object that is freed as a result + of this operation is added to the node cache where + it can be used again. + Remove(NULL) is a harmless no-op - but probably is a wart. +*/ +void *CBaseList::RemoveI(POSITION pos) +{ + /* Lock the critical section before continuing */ + + // ASSERT (pos!=NULL); // Removing NULL is to be harmless! + if (pos==NULL) return NULL; + + + CNode *pCurrent = (CNode *) pos; + ASSERT(pCurrent != NULL); + + /* Update the previous node */ + + CNode *pNode = pCurrent->Prev(); + if (pNode == NULL) { + m_pFirst = pCurrent->Next(); + } else { + pNode->SetNext(pCurrent->Next()); + } + + /* Update the following node */ + + pNode = pCurrent->Next(); + if (pNode == NULL) { + m_pLast = pCurrent->Prev(); + } else { + pNode->SetPrev(pCurrent->Prev()); + } + + /* Get the object this node was looking after */ + + void *pObject = pCurrent->GetData(); + + // ASSERT(pObject != NULL); // NULL pointers in the list are allowed. + + /* Try and add the node object to the cache - + a NULL return code from the cache means we ran out of room. + The cache size is fixed by a constructor argument when the + list is created and defaults to DEFAULTCACHE. + This means that the cache will have room for this many + node objects. So if you have a list of media samples + and you know there will never be more than five active at + any given time of them for example then override the default + constructor + */ + + m_Cache.AddToCache(pCurrent); + + /* If the list is empty then reset the list event */ + + --m_Count; + ASSERT(m_Count >= 0); + return pObject; +} // Remove + + + +/* Add this object to the tail end of our list + Return the new tail position. +*/ + +POSITION CBaseList::AddTailI(void *pObject) +{ + /* Lock the critical section before continuing */ + + CNode *pNode; + // ASSERT(pObject); // NULL pointers in the list are allowed. + + /* If there is a node objects in the cache then use + that otherwise we will have to create a new one */ + + pNode = (CNode *) m_Cache.RemoveFromCache(); + if (pNode == NULL) { + pNode = new CNode; + } + + /* Check we have a valid object */ + + if (pNode == NULL) { + return NULL; + } + + /* Initialise all the CNode object + just in case it came from the cache + */ + + pNode->SetData(pObject); + pNode->SetNext(NULL); + pNode->SetPrev(m_pLast); + + if (m_pLast == NULL) { + m_pFirst = pNode; + } else { + m_pLast->SetNext(pNode); + } + + /* Set the new last node pointer and also increment the number + of list entries, the critical section is unlocked when we + exit the function + */ + + m_pLast = pNode; + ++m_Count; + + return (POSITION) pNode; +} // AddTail(object) + + + +/* Add this object to the head end of our list + Return the new head position. +*/ +POSITION CBaseList::AddHeadI(void *pObject) +{ + CNode *pNode; + // ASSERT(pObject); // NULL pointers in the list are allowed. + + /* If there is a node objects in the cache then use + that otherwise we will have to create a new one */ + + pNode = (CNode *) m_Cache.RemoveFromCache(); + if (pNode == NULL) { + pNode = new CNode; + } + + /* Check we have a valid object */ + + if (pNode == NULL) { + return NULL; + } + + /* Initialise all the CNode object + just in case it came from the cache + */ + + pNode->SetData(pObject); + + /* chain it in (set four pointers) */ + pNode->SetPrev(NULL); + pNode->SetNext(m_pFirst); + + if (m_pFirst == NULL) { + m_pLast = pNode; + } else { + m_pFirst->SetPrev(pNode); + } + m_pFirst = pNode; + + ++m_Count; + + return (POSITION) pNode; +} // AddHead(object) + + + +/* Add all the elements in *pList to the tail of this list. + Return TRUE if it all worked, FALSE if it didn't. + If it fails some elements may have been added. +*/ +BOOL CBaseList::AddTail(CBaseList *pList) +{ + /* lock the object before starting then enumerate + each entry in the source list and add them one by one to + our list (while still holding the object lock) + Lock the other list too. + */ + POSITION pos = pList->GetHeadPositionI(); + + while (pos) { + if (NULL == AddTailI(pList->GetNextI(pos))) { + return FALSE; + } + } + return TRUE; +} // AddTail(list) + + + +/* Add all the elements in *pList to the head of this list. + Return TRUE if it all worked, FALSE if it didn't. + If it fails some elements may have been added. +*/ +BOOL CBaseList::AddHead(CBaseList *pList) +{ + /* lock the object before starting then enumerate + each entry in the source list and add them one by one to + our list (while still holding the object lock) + Lock the other list too. + + To avoid reversing the list, traverse it backwards. + */ + + POSITION pos; + + INTERNALREVERSETRAVERSELIST(*pList, pos) { + if (NULL== AddHeadI(pList->GetI(pos))){ + return FALSE; + } + } + return TRUE; +} // AddHead(list) + + + +/* Add the object after position p + p is still valid after the operation. + AddAfter(NULL,x) adds x to the start - same as AddHead + Return the position of the new object, NULL if it failed +*/ +POSITION CBaseList::AddAfterI(POSITION pos, void * pObj) +{ + if (pos==NULL) + return AddHeadI(pObj); + + /* As someone else might be furkling with the list - + Lock the critical section before continuing + */ + CNode *pAfter = (CNode *) pos; + ASSERT(pAfter != NULL); + if (pAfter==m_pLast) + return AddTailI(pObj); + + /* set pnode to point to a new node, preferably from the cache */ + + CNode *pNode = (CNode *) m_Cache.RemoveFromCache(); + if (pNode == NULL) { + pNode = new CNode; + } + + /* Check we have a valid object */ + + if (pNode == NULL) { + return NULL; + } + + /* Initialise all the CNode object + just in case it came from the cache + */ + + pNode->SetData(pObj); + + /* It is to be added to the middle of the list - there is a before + and after node. Chain it after pAfter, before pBefore. + */ + CNode * pBefore = pAfter->Next(); + ASSERT(pBefore != NULL); + + /* chain it in (set four pointers) */ + pNode->SetPrev(pAfter); + pNode->SetNext(pBefore); + pBefore->SetPrev(pNode); + pAfter->SetNext(pNode); + + ++m_Count; + + return (POSITION) pNode; + +} // AddAfter(object) + + + +BOOL CBaseList::AddAfter(POSITION p, CBaseList *pList) +{ + POSITION pos; + INTERNALTRAVERSELIST(*pList, pos) { + /* p follows along the elements being added */ + p = AddAfterI(p, pList->GetI(pos)); + if (p==NULL) return FALSE; + } + return TRUE; +} // AddAfter(list) + + + +/* Mirror images: + Add the element or list after position p. + p is still valid after the operation. + AddBefore(NULL,x) adds x to the end - same as AddTail +*/ +POSITION CBaseList::AddBeforeI(POSITION pos, void * pObj) +{ + if (pos==NULL) + return AddTailI(pObj); + + /* set pnode to point to a new node, preferably from the cache */ + + CNode *pBefore = (CNode *) pos; + ASSERT(pBefore != NULL); + if (pBefore==m_pFirst) + return AddHeadI(pObj); + + CNode * pNode = (CNode *) m_Cache.RemoveFromCache(); + if (pNode == NULL) { + pNode = new CNode; + } + + /* Check we have a valid object */ + + if (pNode == NULL) { + return NULL; + } + + /* Initialise all the CNode object + just in case it came from the cache + */ + + pNode->SetData(pObj); + + /* It is to be added to the middle of the list - there is a before + and after node. Chain it after pAfter, before pBefore. + */ + + CNode * pAfter = pBefore->Prev(); + ASSERT(pAfter != NULL); + + /* chain it in (set four pointers) */ + pNode->SetPrev(pAfter); + pNode->SetNext(pBefore); + pBefore->SetPrev(pNode); + pAfter->SetNext(pNode); + + ++m_Count; + + return (POSITION) pNode; + +} // Addbefore(object) + + + +BOOL CBaseList::AddBefore(POSITION p, CBaseList *pList) +{ + POSITION pos; + INTERNALREVERSETRAVERSELIST(*pList, pos) { + /* p follows along the elements being added */ + p = AddBeforeI(p, pList->GetI(pos)); + if (p==NULL) return FALSE; + } + return TRUE; +} // AddBefore(list) + + + +/* Split *this after position p in *this + Retain as *this the tail portion of the original *this + Add the head portion to the tail end of *pList + Return TRUE if it all worked, FALSE if it didn't. + + e.g. + foo->MoveToTail(foo->GetHeadPosition(), bar); + moves one element from the head of foo to the tail of bar + foo->MoveToTail(NULL, bar); + is a no-op + foo->MoveToTail(foo->GetTailPosition, bar); + concatenates foo onto the end of bar and empties foo. + + A better, except excessively long name might be + MoveElementsFromHeadThroughPositionToOtherTail +*/ +BOOL CBaseList::MoveToTail + (POSITION pos, CBaseList *pList) +{ + /* Algorithm: + Note that the elements (including their order) in the concatenation + of *pList to the head of *this is invariant. + 1. Count elements to be moved + 2. Join *pList onto the head of this to make one long chain + 3. Set first/Last pointers in *this and *pList + 4. Break the chain at the new place + 5. Adjust counts + 6. Set/Reset any events + */ + + if (pos==NULL) return TRUE; // no-op. Eliminates special cases later. + + + /* Make cMove the number of nodes to move */ + CNode * p = (CNode *)pos; + int cMove = 0; // number of nodes to move + while(p!=NULL) { + p = p->Prev(); + ++cMove; + } + + + /* Join the two chains together */ + if (pList->m_pLast!=NULL) + pList->m_pLast->SetNext(m_pFirst); + if (m_pFirst!=NULL) + m_pFirst->SetPrev(pList->m_pLast); + + + /* set first and last pointers */ + p = (CNode *)pos; + + if (pList->m_pFirst==NULL) + pList->m_pFirst = m_pFirst; + m_pFirst = p->Next(); + if (m_pFirst==NULL) + m_pLast = NULL; + pList->m_pLast = p; + + + /* Break the chain after p to create the new pieces */ + if (m_pFirst!=NULL) + m_pFirst->SetPrev(NULL); + p->SetNext(NULL); + + + /* Adjust the counts */ + m_Count -= cMove; + pList->m_Count += cMove; + + return TRUE; + +} // MoveToTail + + + +/* Mirror image of MoveToTail: + Split *this before position p in *this. + Retain in *this the head portion of the original *this + Add the tail portion to the start (i.e. head) of *pList + Return TRUE if it all worked, FALSE if it didn't. + + e.g. + foo->MoveToHead(foo->GetTailPosition(), bar); + moves one element from the tail of foo to the head of bar + foo->MoveToHead(NULL, bar); + is a no-op + foo->MoveToHead(foo->GetHeadPosition, bar); + concatenates foo onto the start of bar and empties foo. +*/ +BOOL CBaseList::MoveToHead + (POSITION pos, CBaseList *pList) +{ + + /* See the comments on the algorithm in MoveToTail */ + + if (pos==NULL) return TRUE; // no-op. Eliminates special cases later. + + /* Make cMove the number of nodes to move */ + CNode * p = (CNode *)pos; + int cMove = 0; // number of nodes to move + while(p!=NULL) { + p = p->Next(); + ++cMove; + } + + + /* Join the two chains together */ + if (pList->m_pFirst!=NULL) + pList->m_pFirst->SetPrev(m_pLast); + if (m_pLast!=NULL) + m_pLast->SetNext(pList->m_pFirst); + + + /* set first and last pointers */ + p = (CNode *)pos; + + + if (pList->m_pLast==NULL) + pList->m_pLast = m_pLast; + + m_pLast = p->Prev(); + if (m_pLast==NULL) + m_pFirst = NULL; + pList->m_pFirst = p; + + + /* Break the chain after p to create the new pieces */ + if (m_pLast!=NULL) + m_pLast->SetNext(NULL); + p->SetPrev(NULL); + + + /* Adjust the counts */ + m_Count -= cMove; + pList->m_Count += cMove; + + return TRUE; + +} // MoveToHead + + + +/* Reverse the order of the [pointers to] objects in *this +*/ +void CBaseList::Reverse() +{ + /* algorithm: + The obvious booby trap is that you flip pointers around and lose + addressability to the node that you are going to process next. + The easy way to avoid this is do do one chain at a time. + + Run along the forward chain, + For each node, set the reverse pointer to the one ahead of us. + The reverse chain is now a copy of the old forward chain, including + the NULL termination. + + Run along the reverse chain (i.e. old forward chain again) + For each node set the forward pointer of the node ahead to point back + to the one we're standing on. + The first node needs special treatment, + it's new forward pointer is NULL. + Finally set the First/Last pointers + + */ + CNode * p; + + // Yes we COULD use a traverse, but it would look funny! + p = m_pFirst; + while (p!=NULL) { + CNode * q; + q = p->Next(); + p->SetNext(p->Prev()); + p->SetPrev(q); + p = q; + } + + p = m_pFirst; + m_pFirst = m_pLast; + m_pLast = p; + + +#if 0 // old version + + if (m_pFirst==NULL) return; // empty list + if (m_pFirst->Next()==NULL) return; // single node list + + + /* run along forward chain */ + for ( p = m_pFirst + ; p!=NULL + ; p = p->Next() + ){ + p->SetPrev(p->Next()); + } + + + /* special case first element */ + m_pFirst->SetNext(NULL); // fix the old first element + + + /* run along new reverse chain i.e. old forward chain again */ + for ( p = m_pFirst // start at the old first element + ; p->Prev()!=NULL // while there's a node still to be set + ; p = p->Prev() // work in the same direction as before + ){ + p->Prev()->SetNext(p); + } + + + /* fix forward and reverse pointers + - the triple XOR swap would work but all the casts look hideous */ + p = m_pFirst; + m_pFirst = m_pLast; + m_pLast = p; +#endif + +} // Reverse diff --git a/stepmania/src/BaseClasses/wxlist.h b/stepmania/src/BaseClasses/wxlist.h new file mode 100644 index 0000000000..3f4c190435 --- /dev/null +++ b/stepmania/src/BaseClasses/wxlist.h @@ -0,0 +1,551 @@ +//------------------------------------------------------------------------------ +// File: WXList.h +// +// Desc: DirectShow base classes - defines a non-MFC generic template list +// class. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +/* A generic list of pointers to objects. + No storage management or copying is done on the objects pointed to. + Objectives: avoid using MFC libraries in ndm kernel mode and + provide a really useful list type. + + The class is thread safe in that separate threads may add and + delete items in the list concurrently although the application + must ensure that constructor and destructor access is suitably + synchronised. An application can cause deadlock with operations + which use two lists by simultaneously calling + list1->Operation(list2) and list2->Operation(list1). So don't! + + The names must not conflict with MFC classes as an application + may use both. + */ + +#ifndef __WXLIST__ +#define __WXLIST__ + + /* A POSITION represents (in some fashion that's opaque) a cursor + on the list that can be set to identify any element. NULL is + a valid value and several operations regard NULL as the position + "one step off the end of the list". (In an n element list there + are n+1 places to insert and NULL is that "n+1-th" value). + The POSITION of an element in the list is only invalidated if + that element is deleted. Move operations may mean that what + was a valid POSITION in one list is now a valid POSITION in + a different list. + + Some operations which at first sight are illegal are allowed as + harmless no-ops. For instance RemoveHead is legal on an empty + list and it returns NULL. This allows an atomic way to test if + there is an element there, and if so, get it. The two operations + AddTail and RemoveHead thus implement a MONITOR (See Hoare's paper). + + Single element operations return POSITIONs, non-NULL means it worked. + whole list operations return a BOOL. TRUE means it all worked. + + This definition is the same as the POSITION type for MFCs, so we must + avoid defining it twice. + */ +#ifndef __AFX_H__ +struct __POSITION { int unused; }; +typedef __POSITION* POSITION; +#endif + +const int DEFAULTCACHE = 10; /* Default node object cache size */ + +/* A class representing one node in a list. + Each node knows a pointer to it's adjacent nodes and also a pointer + to the object that it looks after. + All of these pointers can be retrieved or set through member functions. +*/ +class CBaseList +#ifdef DEBUG + : public CBaseObject +#endif +{ + /* Making these classes inherit from CBaseObject does nothing + functionally but it allows us to check there are no memory + leaks in debug builds. + */ + +public: + +#ifdef DEBUG + class CNode : public CBaseObject { +#else + class CNode { +#endif + + CNode *m_pPrev; /* Previous node in the list */ + CNode *m_pNext; /* Next node in the list */ + void *m_pObject; /* Pointer to the object */ + + public: + + /* Constructor - initialise the object's pointers */ + CNode() +#ifdef DEBUG + : CBaseObject(NAME("List node")) +#endif + { + }; + + + /* Return the previous node before this one */ + CNode *Prev() const { return m_pPrev; }; + + + /* Return the next node after this one */ + CNode *Next() const { return m_pNext; }; + + + /* Set the previous node before this one */ + void SetPrev(CNode *p) { m_pPrev = p; }; + + + /* Set the next node after this one */ + void SetNext(CNode *p) { m_pNext = p; }; + + + /* Get the pointer to the object for this node */ + void *GetData() const { return m_pObject; }; + + + /* Set the pointer to the object for this node */ + void SetData(void *p) { m_pObject = p; }; + }; + + class CNodeCache + { + public: + CNodeCache(INT iCacheSize) : m_iCacheSize(iCacheSize), + m_pHead(NULL), + m_iUsed(0) + {}; + ~CNodeCache() { + CNode *pNode = m_pHead; + while (pNode) { + CNode *pCurrent = pNode; + pNode = pNode->Next(); + delete pCurrent; + } + }; + void AddToCache(CNode *pNode) + { + if (m_iUsed < m_iCacheSize) { + pNode->SetNext(m_pHead); + m_pHead = pNode; + m_iUsed++; + } else { + delete pNode; + } + }; + CNode *RemoveFromCache() + { + CNode *pNode = m_pHead; + if (pNode != NULL) { + m_pHead = pNode->Next(); + m_iUsed--; + ASSERT(m_iUsed >= 0); + } else { + ASSERT(m_iUsed == 0); + } + return pNode; + }; + private: + INT m_iCacheSize; + INT m_iUsed; + CNode *m_pHead; + }; + +protected: + + CNode* m_pFirst; /* Pointer to first node in the list */ + CNode* m_pLast; /* Pointer to the last node in the list */ + LONG m_Count; /* Number of nodes currently in the list */ + +private: + + CNodeCache m_Cache; /* Cache of unused node pointers */ + +private: + + /* These override the default copy constructor and assignment + operator for all list classes. They are in the private class + declaration section so that anybody trying to pass a list + object by value will generate a compile time error of + "cannot access the private member function". If these were + not here then the compiler will create default constructors + and assignment operators which when executed first take a + copy of all member variables and then during destruction + delete them all. This must not be done for any heap + allocated data. + */ + CBaseList(const CBaseList &refList); + CBaseList &operator=(const CBaseList &refList); + +public: + + CBaseList(TCHAR *pName, + INT iItems); + + CBaseList(TCHAR *pName); +#ifdef UNICODE + CBaseList(CHAR *pName, + INT iItems); + + CBaseList(CHAR *pName); +#endif + ~CBaseList(); + + /* Remove all the nodes from *this i.e. make the list empty */ + void RemoveAll(); + + + /* Return a cursor which identifies the first element of *this */ + POSITION GetHeadPositionI() const; + + + /* Return a cursor which identifies the last element of *this */ + POSITION GetTailPositionI() const; + + + /* Return the number of objects in *this */ + int GetCountI() const; + +protected: + /* Return the pointer to the object at rp, + Update rp to the next node in *this + but make it NULL if it was at the end of *this. + This is a wart retained for backwards compatibility. + GetPrev is not implemented. + Use Next, Prev and Get separately. + */ + void *GetNextI(POSITION& rp) const; + + + /* Return a pointer to the object at p + Asking for the object at NULL will return NULL harmlessly. + */ + void *GetI(POSITION p) const; + +public: + /* return the next / prev position in *this + return NULL when going past the end/start. + Next(NULL) is same as GetHeadPosition() + Prev(NULL) is same as GetTailPosition() + An n element list therefore behaves like a n+1 element + cycle with NULL at the start/end. + + !!WARNING!! - This handling of NULL is DIFFERENT from GetNext. + + Some reasons are: + 1. For a list of n items there are n+1 positions to insert + These are conveniently encoded as the n POSITIONs and NULL. + 2. If you are keeping a list sorted (fairly common) and you + search forward for an element to insert before and don't + find it you finish up with NULL as the element before which + to insert. You then want that NULL to be a valid POSITION + so that you can insert before it and you want that insertion + point to mean the (n+1)-th one that doesn't have a POSITION. + (symmetrically if you are working backwards through the list). + 3. It simplifies the algebra which the methods generate. + e.g. AddBefore(p,x) is identical to AddAfter(Prev(p),x) + in ALL cases. All the other arguments probably are reflections + of the algebraic point. + */ + POSITION Next(POSITION pos) const + { + if (pos == NULL) { + return (POSITION) m_pFirst; + } + CNode *pn = (CNode *) pos; + return (POSITION) pn->Next(); + } //Next + + // See Next + POSITION Prev(POSITION pos) const + { + if (pos == NULL) { + return (POSITION) m_pLast; + } + CNode *pn = (CNode *) pos; + return (POSITION) pn->Prev(); + } //Prev + + + /* Return the first position in *this which holds the given + pointer. Return NULL if the pointer was not not found. + */ +protected: + POSITION FindI( void * pObj) const; + + // ??? Should there be (or even should there be only) + // ??? POSITION FindNextAfter(void * pObj, POSITION p) + // ??? And of course FindPrevBefore too. + // ??? List.Find(&Obj) then becomes List.FindNextAfter(&Obj, NULL) + + + /* Remove the first node in *this (deletes the pointer to its + object from the list, does not free the object itself). + Return the pointer to its object. + If *this was already empty it will harmlessly return NULL. + */ + void *RemoveHeadI(); + + + /* Remove the last node in *this (deletes the pointer to its + object from the list, does not free the object itself). + Return the pointer to its object. + If *this was already empty it will harmlessly return NULL. + */ + void *RemoveTailI(); + + + /* Remove the node identified by p from the list (deletes the pointer + to its object from the list, does not free the object itself). + Asking to Remove the object at NULL will harmlessly return NULL. + Return the pointer to the object removed. + */ + void *RemoveI(POSITION p); + + /* Add single object *pObj to become a new last element of the list. + Return the new tail position, NULL if it fails. + If you are adding a COM objects, you might want AddRef it first. + Other existing POSITIONs in *this are still valid + */ + POSITION AddTailI(void * pObj); +public: + + + /* Add all the elements in *pList to the tail of *this. + This duplicates all the nodes in *pList (i.e. duplicates + all its pointers to objects). It does not duplicate the objects. + If you are adding a list of pointers to a COM object into the list + it's a good idea to AddRef them all it when you AddTail it. + Return TRUE if it all worked, FALSE if it didn't. + If it fails some elements may have been added. + Existing POSITIONs in *this are still valid + + If you actually want to MOVE the elements, use MoveToTail instead. + */ + BOOL AddTail(CBaseList *pList); + + + /* Mirror images of AddHead: */ + + /* Add single object to become a new first element of the list. + Return the new head position, NULL if it fails. + Existing POSITIONs in *this are still valid + */ +protected: + POSITION AddHeadI(void * pObj); +public: + + /* Add all the elements in *pList to the head of *this. + Same warnings apply as for AddTail. + Return TRUE if it all worked, FALSE if it didn't. + If it fails some of the objects may have been added. + + If you actually want to MOVE the elements, use MoveToHead instead. + */ + BOOL AddHead(CBaseList *pList); + + + /* Add the object *pObj to *this after position p in *this. + AddAfter(NULL,x) adds x to the start - equivalent to AddHead + Return the position of the object added, NULL if it failed. + Existing POSITIONs in *this are undisturbed, including p. + */ +protected: + POSITION AddAfterI(POSITION p, void * pObj); +public: + + /* Add the list *pList to *this after position p in *this + AddAfter(NULL,x) adds x to the start - equivalent to AddHead + Return TRUE if it all worked, FALSE if it didn't. + If it fails, some of the objects may be added + Existing POSITIONs in *this are undisturbed, including p. + */ + BOOL AddAfter(POSITION p, CBaseList *pList); + + + /* Mirror images: + Add the object *pObj to this-List after position p in *this. + AddBefore(NULL,x) adds x to the end - equivalent to AddTail + Return the position of the new object, NULL if it fails + Existing POSITIONs in *this are undisturbed, including p. + */ + protected: + POSITION AddBeforeI(POSITION p, void * pObj); + public: + + /* Add the list *pList to *this before position p in *this + AddAfter(NULL,x) adds x to the start - equivalent to AddHead + Return TRUE if it all worked, FALSE if it didn't. + If it fails, some of the objects may be added + Existing POSITIONs in *this are undisturbed, including p. + */ + BOOL AddBefore(POSITION p, CBaseList *pList); + + + /* Note that AddAfter(p,x) is equivalent to AddBefore(Next(p),x) + even in cases where p is NULL or Next(p) is NULL. + Similarly for mirror images etc. + This may make it easier to argue about programs. + */ + + + + /* The following operations do not copy any elements. + They move existing blocks of elements around by switching pointers. + They are fairly efficient for long lists as for short lists. + (Alas, the Count slows things down). + + They split the list into two parts. + One part remains as the original list, the other part + is appended to the second list. There are eight possible + variations: + Split the list {after/before} a given element + keep the {head/tail} portion in the original list + append the rest to the {head/tail} of the new list. + + Since After is strictly equivalent to Before Next + we are not in serious need of the Before/After variants. + That leaves only four. + + If you are processing a list left to right and dumping + the bits that you have processed into another list as + you go, the Tail/Tail variant gives the most natural result. + If you are processing in reverse order, Head/Head is best. + + By using NULL positions and empty lists judiciously either + of the other two can be built up in two operations. + + The definition of NULL (see Next/Prev etc) means that + degenerate cases include + "move all elements to new list" + "Split a list into two lists" + "Concatenate two lists" + (and quite a few no-ops) + + !!WARNING!! The type checking won't buy you much if you get list + positions muddled up - e.g. use a POSITION that's in a different + list and see what a mess you get! + */ + + /* Split *this after position p in *this + Retain as *this the tail portion of the original *this + Add the head portion to the tail end of *pList + Return TRUE if it all worked, FALSE if it didn't. + + e.g. + foo->MoveToTail(foo->GetHeadPosition(), bar); + moves one element from the head of foo to the tail of bar + foo->MoveToTail(NULL, bar); + is a no-op, returns NULL + foo->MoveToTail(foo->GetTailPosition, bar); + concatenates foo onto the end of bar and empties foo. + + A better, except excessively long name might be + MoveElementsFromHeadThroughPositionToOtherTail + */ + BOOL MoveToTail(POSITION pos, CBaseList *pList); + + + /* Mirror image: + Split *this before position p in *this. + Retain in *this the head portion of the original *this + Add the tail portion to the start (i.e. head) of *pList + + e.g. + foo->MoveToHead(foo->GetTailPosition(), bar); + moves one element from the tail of foo to the head of bar + foo->MoveToHead(NULL, bar); + is a no-op, returns NULL + foo->MoveToHead(foo->GetHeadPosition, bar); + concatenates foo onto the start of bar and empties foo. + */ + BOOL MoveToHead(POSITION pos, CBaseList *pList); + + + /* Reverse the order of the [pointers to] objects in *this + */ + void Reverse(); + + + /* set cursor to the position of each element of list in turn */ + #define TRAVERSELIST(list, cursor) \ + for ( cursor = (list).GetHeadPosition() \ + ; cursor!=NULL \ + ; cursor = (list).Next(cursor) \ + ) + + + /* set cursor to the position of each element of list in turn + in reverse order + */ + #define REVERSETRAVERSELIST(list, cursor) \ + for ( cursor = (list).GetTailPosition() \ + ; cursor!=NULL \ + ; cursor = (list).Prev(cursor) \ + ) + +}; // end of class declaration + +template class CGenericList : public CBaseList +{ +public: + CGenericList(TCHAR *pName, + INT iItems, + BOOL bLock = TRUE, + BOOL bAlert = FALSE) : + CBaseList(pName, iItems) { + UNREFERENCED_PARAMETER(bAlert); + UNREFERENCED_PARAMETER(bLock); + }; + CGenericList(TCHAR *pName) : + CBaseList(pName) { + }; + + POSITION GetHeadPosition() const { return (POSITION)m_pFirst; } + POSITION GetTailPosition() const { return (POSITION)m_pLast; } + int GetCount() const { return m_Count; } + + OBJECT *GetNext(POSITION& rp) const { return (OBJECT *) GetNextI(rp); } + + OBJECT *Get(POSITION p) const { return (OBJECT *) GetI(p); } + OBJECT *GetHead() const { return Get(GetHeadPosition()); } + + OBJECT *RemoveHead() { return (OBJECT *) RemoveHeadI(); } + + OBJECT *RemoveTail() { return (OBJECT *) RemoveTailI(); } + + OBJECT *Remove(POSITION p) { return (OBJECT *) RemoveI(p); } + POSITION AddBefore(POSITION p, OBJECT * pObj) { return AddBeforeI(p, pObj); } + POSITION AddAfter(POSITION p, OBJECT * pObj) { return AddAfterI(p, pObj); } + POSITION AddHead(OBJECT * pObj) { return AddHeadI(pObj); } + POSITION AddTail(OBJECT * pObj) { return AddTailI(pObj); } + BOOL AddTail(CGenericList *pList) + { return CBaseList::AddTail((CBaseList *) pList); } + BOOL AddHead(CGenericList *pList) + { return CBaseList::AddHead((CBaseList *) pList); } + BOOL AddAfter(POSITION p, CGenericList *pList) + { return CBaseList::AddAfter(p, (CBaseList *) pList); }; + BOOL AddBefore(POSITION p, CGenericList *pList) + { return CBaseList::AddBefore(p, (CBaseList *) pList); }; + POSITION Find( OBJECT * pObj) const { return FindI(pObj); } +}; // end of class declaration + + + +/* These define the standard list types */ + +typedef CGenericList CBaseObjectList; +typedef CGenericList CBaseInterfaceList; + +#endif /* __WXLIST__ */ + diff --git a/stepmania/src/BaseClasses/wxutil.cpp b/stepmania/src/BaseClasses/wxutil.cpp new file mode 100644 index 0000000000..e1d440a222 --- /dev/null +++ b/stepmania/src/BaseClasses/wxutil.cpp @@ -0,0 +1,1210 @@ +//------------------------------------------------------------------------------ +// File: WXUtil.cpp +// +// Desc: DirectShow base classes - implements helper classes for building +// multimedia filters. +// +// Copyright (c) 1992-2001 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 + +// --- CAMEvent ----------------------- +CAMEvent::CAMEvent(BOOL fManualReset) { + m_hEvent = CreateEvent(NULL, fManualReset, FALSE, NULL); +} + +CAMEvent::~CAMEvent() { + if(m_hEvent) { + EXECUTE_ASSERT(CloseHandle(m_hEvent)); + } +} + + +// --- CAMMsgEvent ----------------------- +// One routine. The rest is handled in CAMEvent + +BOOL CAMMsgEvent::WaitMsg(DWORD dwTimeout) { + // wait for the event to be signalled, or for the + // timeout (in MS) to expire. allow SENT messages + // to be processed while we wait + DWORD dwWait; + DWORD dwStartTime=0; + + // set the waiting period. + DWORD dwWaitTime = dwTimeout; + + // the timeout will eventually run down as we iterate + // processing messages. grab the start time so that + // we can calculate elapsed times. + if(dwWaitTime != INFINITE) { + dwStartTime = timeGetTime(); + } + + do { + dwWait = MsgWaitForMultipleObjects(1,&m_hEvent,FALSE, dwWaitTime, QS_SENDMESSAGE); + if(dwWait == WAIT_OBJECT_0 + 1) { + MSG Message; + PeekMessage(&Message,NULL,0,0,PM_NOREMOVE); + + // If we have an explicit length of time to wait calculate + // the next wake up point - which might be now. + // If dwTimeout is INFINITE, it stays INFINITE + if(dwWaitTime != INFINITE) { + + DWORD dwElapsed = timeGetTime()-dwStartTime; + + dwWaitTime = + (dwElapsed >= dwTimeout) + ? 0 // wake up with WAIT_TIMEOUT + : dwTimeout-dwElapsed; + } + } + } while(dwWait == WAIT_OBJECT_0 + 1); + + // return TRUE if we woke on the event handle, + // FALSE if we timed out. + return (dwWait == WAIT_OBJECT_0); +} + +// --- CAMThread ---------------------- + + +CAMThread::CAMThread() + : m_EventSend(TRUE) // must be manual-reset for CheckRequest() +{ + m_hThread = NULL; +} + +CAMThread::~CAMThread() { + Close(); +} + + +// when the thread starts, it calls this function. We unwrap the 'this' +//pointer and call ThreadProc. +DWORD WINAPI +CAMThread::InitialThreadProc(LPVOID pv) { + HRESULT hrCoInit = CAMThread::CoInitializeHelper(); + if(FAILED(hrCoInit)) { + DbgLog((LOG_ERROR, 1, TEXT("CoInitializeEx failed."))); + } + + CAMThread * pThread = (CAMThread *) pv; + + HRESULT hr = pThread->ThreadProc(); + + if(SUCCEEDED(hrCoInit)) { + CoUninitialize(); + } + + return hr; +} + +BOOL +CAMThread::Create() { + DWORD threadid; + + CAutoLock lock(&m_AccessLock); + + if(ThreadExists()) { + return FALSE; + } + + m_hThread = CreateThread(NULL, + 0, + CAMThread::InitialThreadProc, + this, + 0, + &threadid); + + if(!m_hThread) { + return FALSE; + } + + return TRUE; +} + +DWORD +CAMThread::CallWorker(DWORD dwParam) { + // lock access to the worker thread for scope of this object + CAutoLock lock(&m_AccessLock); + + if(!ThreadExists()) { + return (DWORD) E_FAIL; + } + + // set the parameter + m_dwParam = dwParam; + + // signal the worker thread + m_EventSend.Set(); + + // wait for the completion to be signalled + m_EventComplete.Wait(); + + // done - this is the thread's return value + return m_dwReturnVal; +} + +// Wait for a request from the client +DWORD +CAMThread::GetRequest() { + m_EventSend.Wait(); + return m_dwParam; +} + +// is there a request? +BOOL +CAMThread::CheckRequest(DWORD * pParam) { + if(!m_EventSend.Check()) { + return FALSE; + } + else { + if(pParam) { + *pParam = m_dwParam; + } + return TRUE; + } +} + +// reply to the request +void +CAMThread::Reply(DWORD dw) { + m_dwReturnVal = dw; + + // The request is now complete so CheckRequest should fail from + // now on + // + // This event should be reset BEFORE we signal the client or + // the client may Set it before we reset it and we'll then + // reset it (!) + + m_EventSend.Reset(); + + // Tell the client we're finished + + m_EventComplete.Set(); +} + +HRESULT CAMThread::CoInitializeHelper() { + // call CoInitializeEx and tell OLE not to create a window (this + // thread probably won't dispatch messages and will hang on + // broadcast msgs o/w). + // + // If CoInitEx is not available, threads that don't call CoCreate + // aren't affected. Threads that do will have to handle the + // failure. Perhaps we should fall back to CoInitialize and risk + // hanging? + // + + // older versions of ole32.dll don't have CoInitializeEx + + HRESULT hr = E_FAIL; + HINSTANCE hOle = GetModuleHandle(TEXT("ole32.dll")); + if(hOle) { + typedef HRESULT(STDAPICALLTYPE *PCoInitializeEx)( + LPVOID pvReserved, DWORD dwCoInit); + PCoInitializeEx pCoInitializeEx = + (PCoInitializeEx)(GetProcAddress(hOle, "CoInitializeEx")); + if(pCoInitializeEx) { + hr = (*pCoInitializeEx)(0, COINIT_DISABLE_OLE1DDE ); + } + } + else { + // caller must load ole32.dll + DbgBreak("couldn't locate ole32.dll"); + } + + return hr; +} + + +// destructor for CMsgThread - cleans up any messages left in the +// queue when the thread exited +CMsgThread::~CMsgThread() { + if(m_hThread != NULL) { + WaitForSingleObject(m_hThread, INFINITE); + EXECUTE_ASSERT(CloseHandle(m_hThread)); + } + + POSITION pos = m_ThreadQueue.GetHeadPosition(); + while(pos) { + CMsg * pMsg = m_ThreadQueue.GetNext(pos); + delete pMsg; + } + m_ThreadQueue.RemoveAll(); + + if(m_hSem != NULL) { + EXECUTE_ASSERT(CloseHandle(m_hSem)); + } +} + +BOOL +CMsgThread::CreateThread( + ) { + m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); + if(m_hSem == NULL) { + return FALSE; + } + + m_hThread = ::CreateThread(NULL, 0, DefaultThreadProc, + (LPVOID)this, 0, &m_ThreadId); + return m_hThread != NULL; +} + + +// This is the threads message pump. Here we get and dispatch messages to +// clients thread proc until the client refuses to process a message. +// The client returns a non-zero value to stop the message pump, this +// value becomes the threads exit code. + +DWORD WINAPI +CMsgThread::DefaultThreadProc( + LPVOID lpParam + ) { + CMsgThread *lpThis = (CMsgThread *)lpParam; + CMsg msg; + LRESULT lResult; + + // !!! + CoInitialize(NULL); + + // allow a derived class to handle thread startup + lpThis->OnThreadInit(); + + do { + lpThis->GetThreadMsg(&msg); + lResult = lpThis->ThreadMessageProc(msg.uMsg,msg.dwFlags, + msg.lpParam, msg.pEvent); + } while(lResult == 0L); + + // !!! + CoUninitialize(); + + return (DWORD)lResult; +} + + +// Block until the next message is placed on the list m_ThreadQueue. +// copies the message to the message pointed to by *pmsg +void +CMsgThread::GetThreadMsg(CMsg *msg) { + CMsg * pmsg = NULL; + + // keep trying until a message appears + while(TRUE) { { + CAutoLock lck(&m_Lock); + pmsg = m_ThreadQueue.RemoveHead(); + if(pmsg == NULL) { + m_lWaiting++; + } + else { + break; + } + } + // the semaphore will be signalled when it is non-empty + WaitForSingleObject(m_hSem, INFINITE); + } + // copy fields to caller's CMsg + *msg = *pmsg; + + // this CMsg was allocated by the 'new' in PutThreadMsg + delete pmsg; + +} + + +// NOTE: as we need to use the same binaries on Win95 as on NT this code should +// be compiled WITHOUT unicode being defined. Otherwise we will not pick up +// these internal routines and the binary will not run on Win95. + +#ifndef UNICODE + +#pragma warning(disable:4706) // C4706: assignment within conditional expression + +// Windows 95 doesn't implement this, so we provide an implementation. +LPWSTR +WINAPI +lstrcpyWInternal( + LPWSTR lpString1, + LPCWSTR lpString2 + ) { + LPWSTR lpReturn = lpString1; + while(*lpString1++ = *lpString2++); + + return lpReturn; +} + +// Windows 95 doesn't implement this, so we provide an implementation. +LPWSTR +WINAPI +lstrcpynWInternal( + LPWSTR lpString1, + LPCWSTR lpString2, + int iMaxLength + ) { + ASSERT(iMaxLength); + LPWSTR lpReturn = lpString1; + if(iMaxLength) { + while(--iMaxLength && (*lpString1++ = *lpString2++)); + + // If we ran out of room (which will be the case if + // iMaxLength is now 0) we still need to terminate the + // string. + if(!iMaxLength) *lpString1 = L'\0'; + } + return lpReturn; +} + +#pragma warning(default:4706) // C4706: assignment within conditional expression + +int +WINAPI +lstrcmpWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ) { + do { + WCHAR c1 = *lpString1; + WCHAR c2 = *lpString2; + if(c1 != c2) + return (int) c1 - (int) c2; + } while(*lpString1++ && *lpString2++); + return 0; +} + + +int +WINAPI +lstrcmpiWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ) { + do { + WCHAR c1 = *lpString1; + WCHAR c2 = *lpString2; + if(c1 >= L'A' && c1 <= L'Z') + c1 -= (WCHAR) (L'A' - L'a'); + if(c2 >= L'A' && c2 <= L'Z') + c2 -= (WCHAR) (L'A' - L'a'); + + if(c1 != c2) + return (int) c1 - (int) c2; + } while(*lpString1++ && *lpString2++); + + return 0; +} + + +int +WINAPI +lstrlenWInternal( + LPCWSTR lpString + ) { + int i = -1; + while(*(lpString+(++i))) + ; + return i; +} + + +int WINAPIV wsprintfWInternal(LPWSTR wszOut, LPCWSTR pszFmt, ...) { + char fmt[256]; // !!! + char ach[256]; // !!! + int i; + + va_list va; + va_start(va, pszFmt); + WideCharToMultiByte(GetACP(), 0, pszFmt, -1, fmt, 256, NULL, NULL); + i = wvsprintfA(ach, fmt, va); + va_end(va); + + MultiByteToWideChar(CP_ACP, 0, ach, -1, wszOut, i+1); + + return i; +} +#else + +// need to provide the implementations in unicode for non-unicode +// builds linking with the unicode strmbase.lib +LPWSTR WINAPI lstrcpyWInternal( + LPWSTR lpString1, + LPCWSTR lpString2 + ) { + return lstrcpyW(lpString1, lpString2); +} + +LPWSTR WINAPI lstrcpynWInternal( + LPWSTR lpString1, + LPCWSTR lpString2, + int iMaxLength + ) { + return lstrcpynW(lpString1, lpString2, iMaxLength); +} + +int WINAPI lstrcmpWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ) { + return lstrcmpW(lpString1, lpString2); +} + + +int WINAPI lstrcmpiWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ) { + return lstrcmpiW(lpString1, lpString2); +} + + +int WINAPI lstrlenWInternal( + LPCWSTR lpString + ) { + return lstrlenW(lpString); +} + + +int WINAPIV wsprintfWInternal( + LPWSTR wszOut, LPCWSTR pszFmt, ...) { + va_list va; + va_start(va, pszFmt); + int i = wvsprintfW(wszOut, pszFmt, va); + va_end(va); + return i; +} +#endif + + +// Helper function - convert int to WSTR +void WINAPI IntToWstr(int i, LPWSTR wstr) { +#ifdef UNICODE + wsprintf(wstr, L"%d", i); +#else + TCHAR temp[32]; + wsprintf(temp, "%d", i); + MultiByteToWideChar(CP_ACP, 0, temp, -1, wstr, 32); +#endif +} // IntToWstr + + +#if 0 +void * memchrInternal(const void *pv, int c, size_t sz) { + BYTE *pb = (BYTE *) pv; + while(sz--) { + if(*pb == c) + return (void *) pb; + pb++; + } + return NULL; +} +#endif + + +#define MEMORY_ALIGNMENT 4 +#define MEMORY_ALIGNMENT_LOG2 2 +#define MEMORY_ALIGNMENT_MASK MEMORY_ALIGNMENT - 1 + +void * __stdcall memmoveInternal(void * dst, const void * src, size_t count) { + void * ret = dst; + +#ifdef _X86_ + if(dst <= src || (char *)dst >= ((char *)src + count)) { + + /* + * Non-Overlapping Buffers + * copy from lower addresses to higher addresses + */ + _asm { + mov esi,src + mov edi,dst + mov ecx,count + cld + mov edx,ecx + and edx,MEMORY_ALIGNMENT_MASK + shr ecx,MEMORY_ALIGNMENT_LOG2 + rep movsd + or ecx,edx + jz memmove_done + rep movsb + memmove_done: + } + } + else { + + /* + * Overlapping Buffers + * copy from higher addresses to lower addresses + */ + _asm { + mov esi,src + mov edi,dst + mov ecx,count + std + add esi,ecx + add edi,ecx + dec esi + dec edi + rep movsb + cld + } + } +#else + MoveMemory(dst, src, count); +#endif + + return ret; +} + +/* 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; +} + +#ifdef DEBUG +/******************************Public*Routine******************************\ +* Debug CCritSec helpers +* +* We provide debug versions of the Constructor, destructor, Lock and Unlock +* routines. The debug code tracks who owns each critical section by +* maintaining a depth count. +* +* History: +* +\**************************************************************************/ + +CCritSec::CCritSec() { + InitializeCriticalSection(&m_CritSec); + m_currentOwner = m_lockCount = 0; + m_fTrace = FALSE; +} + +CCritSec::~CCritSec() { + DeleteCriticalSection(&m_CritSec); +} + +void CCritSec::Lock() { + UINT tracelevel=3; + DWORD us = GetCurrentThreadId(); + DWORD currentOwner = m_currentOwner; + if(currentOwner && (currentOwner != us)) { + // already owned, but not by us + if(m_fTrace) { + DbgLog((LOG_LOCKING, 2, TEXT("Thread %d about to wait for lock %x owned by %d"), + GetCurrentThreadId(), &m_CritSec, currentOwner)); + tracelevel=2; + // if we saw the message about waiting for the critical + // section we ensure we see the message when we get the + // critical section + } + } + EnterCriticalSection(&m_CritSec); + if(0 == m_lockCount++) { + // we now own it for the first time. Set owner information + m_currentOwner = us; + + if(m_fTrace) { + DbgLog((LOG_LOCKING, tracelevel, TEXT("Thread %d now owns lock %x"), m_currentOwner, &m_CritSec)); + } + } +} + +void CCritSec::Unlock() { + if(0 == --m_lockCount) { + // about to be unowned + if(m_fTrace) { + DbgLog((LOG_LOCKING, 3, TEXT("Thread %d releasing lock %x"), m_currentOwner, &m_CritSec)); + } + + m_currentOwner = 0; + } + LeaveCriticalSection(&m_CritSec); +} + +void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace) { + pcCrit->m_fTrace = fTrace; +} + +BOOL WINAPI CritCheckIn(CCritSec * pcCrit) { + return (GetCurrentThreadId() == pcCrit->m_currentOwner); +} + +BOOL WINAPI CritCheckIn(const CCritSec * pcCrit) { + return (GetCurrentThreadId() == pcCrit->m_currentOwner); +} + +BOOL WINAPI CritCheckOut(CCritSec * pcCrit) { + return (GetCurrentThreadId() != pcCrit->m_currentOwner); +} + +BOOL WINAPI CritCheckOut(const CCritSec * pcCrit) { + return (GetCurrentThreadId() != pcCrit->m_currentOwner); +} +#endif + + +STDAPI WriteBSTR(BSTR *pstrDest, LPCWSTR szSrc) { + *pstrDest = SysAllocString(szSrc); + if(!(*pstrDest)) return E_OUTOFMEMORY; + return NOERROR; +} + + +STDAPI FreeBSTR(BSTR* pstr) { + if(*pstr == NULL) return S_FALSE; + SysFreeString(*pstr); + return NOERROR; +} + + +// Return a wide string - allocating memory for it +// Returns: +// S_OK - no error +// E_POINTER - ppszReturn == NULL +// E_OUTOFMEMORY - can't allocate memory for returned string +STDAPI AMGetWideString(LPCWSTR psz, LPWSTR *ppszReturn) { + CheckPointer(ppszReturn, E_POINTER); + ValidateReadWritePtr(ppszReturn, sizeof(LPWSTR)); + DWORD nameLen = sizeof(WCHAR) * (lstrlenW(psz)+1); + *ppszReturn = (LPWSTR)CoTaskMemAlloc(nameLen); + if(*ppszReturn == NULL) { + return E_OUTOFMEMORY; + } + CopyMemory(*ppszReturn, psz, nameLen); + return NOERROR; +} + +// Waits for the HANDLE hObject. While waiting messages sent +// to windows on our thread by SendMessage will be processed. +// Using this function to do waits and mutual exclusion +// avoids some deadlocks in objects with windows. +// Return codes are the same as for WaitForSingleObject +DWORD WINAPI WaitDispatchingMessages( + HANDLE hObject, + DWORD dwWait, + HWND hwnd, + UINT uMsg, + HANDLE hEvent) { + BOOL bPeeked = FALSE; + DWORD dwResult; + DWORD dwStart=0; + DWORD dwThreadPriority=0; + + static UINT uMsgId = 0; + + HANDLE hObjects[2] = { hObject, hEvent }; + if(dwWait != INFINITE && dwWait != 0) { + dwStart = GetTickCount(); + } + for(; ;) { + DWORD nCount = NULL != hEvent ? 2 : 1; + + // Minimize the chance of actually dispatching any messages + // by seeing if we can lock immediately. + dwResult = WaitForMultipleObjects(nCount, hObjects, FALSE, 0); + if(dwResult < WAIT_OBJECT_0 + nCount) { + break; + } + + DWORD dwTimeOut = dwWait; + if(dwTimeOut > 10) { + dwTimeOut = 10; + } + dwResult = MsgWaitForMultipleObjects(nCount, + hObjects, + FALSE, + dwTimeOut, + hwnd == NULL ? QS_SENDMESSAGE : + QS_SENDMESSAGE + QS_POSTMESSAGE); + if(dwResult == WAIT_OBJECT_0 + nCount || + dwResult == WAIT_TIMEOUT && dwTimeOut != dwWait) { + MSG msg; + if(hwnd != NULL) { + while(PeekMessage(&msg, hwnd, uMsg, uMsg, PM_REMOVE)) { + DispatchMessage(&msg); + } + } + // Do this anyway - the previous peek doesn't flush out the + // messages + PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); + + if(dwWait != INFINITE && dwWait != 0) { + DWORD dwNow = GetTickCount(); + + // Working with differences handles wrap-around + DWORD dwDiff = dwNow - dwStart; + if(dwDiff > dwWait) { + dwWait = 0; + } + else { + dwWait -= dwDiff; + } + dwStart = dwNow; + } + if(!bPeeked) { + // Raise our priority to prevent our message queue + // building up + dwThreadPriority = GetThreadPriority(GetCurrentThread()); + if(dwThreadPriority < THREAD_PRIORITY_HIGHEST) { + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); + } + bPeeked = TRUE; + } + } + else { + break; + } + } + if(bPeeked) { + SetThreadPriority(GetCurrentThread(), dwThreadPriority); + if(HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) { + if(uMsgId == 0) { + uMsgId = RegisterWindowMessage(TEXT("AMUnblock")); + } + if(uMsgId != 0) { + MSG msg; + // Remove old ones + while(PeekMessage(&msg, (HWND)-1, uMsgId, uMsgId, PM_REMOVE)) { + } + } + PostThreadMessage(GetCurrentThreadId(), uMsgId, 0, 0); + } + } + return dwResult; +} + +HRESULT AmGetLastErrorToHResult() { + DWORD dwLastError = GetLastError(); + if(dwLastError != 0) { + return HRESULT_FROM_WIN32(dwLastError); + } + else { + return E_FAIL; + } +} + +IUnknown* QzAtlComPtrAssign(IUnknown** pp, IUnknown* lp) { + if(lp != NULL) + lp->AddRef(); + if(*pp) + (*pp)->Release(); + *pp = lp; + return lp; +} + +/****************************************************************************** + +CompatibleTimeSetEvent + + CompatibleTimeSetEvent() sets the TIME_KILL_SYNCHRONOUS flag before calling +timeSetEvent() if the current operating system supports it. TIME_KILL_SYNCHRONOUS +is supported on Windows XP and later operating systems. + +Parameters: +- The same parameters as timeSetEvent(). See timeSetEvent()'s documentation in +the Platform SDK for more information. + +Return Value: +- The same return value as timeSetEvent(). See timeSetEvent()'s documentation in +the Platform SDK for more information. + +******************************************************************************/ +MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent ) { + // + // Building for Windows XP + // ----------------------- + // If you want to target Windows XP specifically to use its new features, + // you must set WINVER=0x501 in the sample project file. You must also + // install the Windows XP Platform SDK, however, to ensure that you have the + // latest header files. For example, this CompatibleTimeSetEvent() method + // uses the new TIME_KILL_SYNCHRONOUS flag only if (WINVER >= 0x501). This flag + // is conditionally defined in the Windows XP Platform SDK in mmsystem.h, + // but only if WINVER is also set to 0x501 when compiling. To prevent build + // issues, upgrade to the latest version of the Platform SDK. + // + +#if WINVER >= 0x0501 + { + static bool fCheckedVersion = false; + static bool fTimeKillSynchronousFlagAvailable = false; + + if(!fCheckedVersion) { + fTimeKillSynchronousFlagAvailable = TimeKillSynchronousFlagAvailable(); + fCheckedVersion = true; + } + + if(fTimeKillSynchronousFlagAvailable) { + fuEvent = fuEvent | TIME_KILL_SYNCHRONOUS; + } + } +#endif // WINVER >= 0x0501 + + return timeSetEvent(uDelay, uResolution, lpTimeProc, dwUser, fuEvent); +} + +bool TimeKillSynchronousFlagAvailable( void ) { + OSVERSIONINFO osverinfo; + + osverinfo.dwOSVersionInfoSize = sizeof(osverinfo); + + if(GetVersionEx(&osverinfo)) { + + // Windows XP's major version is 5 and its' minor version is 1. + // timeSetEvent() started supporting the TIME_KILL_SYNCHRONOUS flag + // in Windows XP. + if((osverinfo.dwMajorVersion > 5) || + ( (osverinfo.dwMajorVersion == 5) && (osverinfo.dwMinorVersion >= 1) )) { + return true; + } + } + + return false; +} + diff --git a/stepmania/src/BaseClasses/wxutil.h b/stepmania/src/BaseClasses/wxutil.h new file mode 100644 index 0000000000..92941e87dd --- /dev/null +++ b/stepmania/src/BaseClasses/wxutil.h @@ -0,0 +1,533 @@ +//------------------------------------------------------------------------------ +// File: WXUtil.h +// +// Desc: DirectShow base classes - defines helper classes and functions for +// building multimedia filters. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __WXUTIL__ +#define __WXUTIL__ + +// eliminate spurious "statement has no effect" warnings. +#pragma warning(disable: 4705) + +// wrapper for whatever critical section we have +class CCritSec { + + // make copy constructor and assignment operator inaccessible + + CCritSec(const CCritSec &refCritSec); + CCritSec &operator=(const CCritSec &refCritSec); + + CRITICAL_SECTION m_CritSec; + +#ifdef DEBUG +public: + DWORD m_currentOwner; + DWORD m_lockCount; + BOOL m_fTrace; // Trace this one +public: + CCritSec(); + ~CCritSec(); + void Lock(); + void Unlock(); +#else + +public: + CCritSec() { + InitializeCriticalSection(&m_CritSec); + }; + + ~CCritSec() { + DeleteCriticalSection(&m_CritSec); + }; + + void Lock() { + EnterCriticalSection(&m_CritSec); + }; + + void Unlock() { + LeaveCriticalSection(&m_CritSec); + }; +#endif +}; + +// +// To make deadlocks easier to track it is useful to insert in the +// code an assertion that says whether we own a critical section or +// not. We make the routines that do the checking globals to avoid +// having different numbers of member functions in the debug and +// retail class implementations of CCritSec. In addition we provide +// a routine that allows usage of specific critical sections to be +// traced. This is NOT on by default - there are far too many. +// + +#ifdef DEBUG + BOOL WINAPI CritCheckIn(CCritSec * pcCrit); + BOOL WINAPI CritCheckIn(const CCritSec * pcCrit); + BOOL WINAPI CritCheckOut(CCritSec * pcCrit); + BOOL WINAPI CritCheckOut(const CCritSec * pcCrit); + void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace); +#else + #define CritCheckIn(x) TRUE + #define CritCheckOut(x) TRUE + #define DbgLockTrace(pc, fT) +#endif + + +// locks a critical section, and unlocks it automatically +// when the lock goes out of scope +class CAutoLock { + + // make copy constructor and assignment operator inaccessible + + CAutoLock(const CAutoLock &refAutoLock); + CAutoLock &operator=(const CAutoLock &refAutoLock); + +protected: + CCritSec * m_pLock; + +public: + CAutoLock(CCritSec * plock) + { + m_pLock = plock; + m_pLock->Lock(); + }; + + ~CAutoLock() { + m_pLock->Unlock(); + }; +}; + + + +// wrapper for event objects +class CAMEvent +{ + + // make copy constructor and assignment operator inaccessible + + CAMEvent(const CAMEvent &refEvent); + CAMEvent &operator=(const CAMEvent &refEvent); + +protected: + HANDLE m_hEvent; +public: + CAMEvent(BOOL fManualReset = FALSE); + ~CAMEvent(); + + // Cast to HANDLE - we don't support this as an lvalue + operator HANDLE () const { return m_hEvent; }; + + void Set() {EXECUTE_ASSERT(SetEvent(m_hEvent));}; + BOOL Wait(DWORD dwTimeout = INFINITE) { + return (WaitForSingleObject(m_hEvent, dwTimeout) == WAIT_OBJECT_0); + }; + void Reset() { ResetEvent(m_hEvent); }; + BOOL Check() { return Wait(0); }; +}; + + +// wrapper for event objects that do message processing +// This adds ONE method to the CAMEvent object to allow sent +// messages to be processed while waiting + +class CAMMsgEvent : public CAMEvent +{ + +public: + + // Allow SEND messages to be processed while waiting + BOOL WaitMsg(DWORD dwTimeout = INFINITE); +}; + +// old name supported for the time being +#define CTimeoutEvent CAMEvent + +// support for a worker thread + +// simple thread class supports creation of worker thread, synchronization +// and communication. Can be derived to simplify parameter passing +class AM_NOVTABLE CAMThread { + + // make copy constructor and assignment operator inaccessible + + CAMThread(const CAMThread &refThread); + CAMThread &operator=(const CAMThread &refThread); + + CAMEvent m_EventSend; + CAMEvent m_EventComplete; + + DWORD m_dwParam; + DWORD m_dwReturnVal; + +protected: + HANDLE m_hThread; + + // thread will run this function on startup + // must be supplied by derived class + virtual DWORD ThreadProc() = 0; + +public: + CAMThread(); + virtual ~CAMThread(); + + CCritSec m_AccessLock; // locks access by client threads + CCritSec m_WorkerLock; // locks access to shared objects + + // thread initially runs this. param is actually 'this'. function + // just gets this and calls ThreadProc + static DWORD WINAPI InitialThreadProc(LPVOID pv); + + // start thread running - error if already running + BOOL Create(); + + // signal the thread, and block for a response + // + DWORD CallWorker(DWORD); + + // accessor thread calls this when done with thread (having told thread + // to exit) + void Close() { + HANDLE hThread = (HANDLE)InterlockedExchangePointer(&m_hThread, 0); + if (hThread) { + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); + } + }; + + // ThreadExists + // Return TRUE if the thread exists. FALSE otherwise + BOOL ThreadExists(void) const + { + if (m_hThread == 0) { + return FALSE; + } else { + return TRUE; + } + } + + // wait for the next request + DWORD GetRequest(); + + // is there a request? + BOOL CheckRequest(DWORD * pParam); + + // reply to the request + void Reply(DWORD); + + // If you want to do WaitForMultipleObjects you'll need to include + // this handle in your wait list or you won't be responsive + HANDLE GetRequestHandle() const { return m_EventSend; }; + + // Find out what the request was + DWORD GetRequestParam() const { return m_dwParam; }; + + // call CoInitializeEx (COINIT_DISABLE_OLE1DDE) if + // available. S_FALSE means it's not available. + static HRESULT CoInitializeHelper(); +}; + + +// CQueue +// +// Implements a simple Queue ADT. The queue contains a finite number of +// objects, access to which is controlled by a semaphore. The semaphore +// is created with an initial count (N). Each time an object is added +// a call to WaitForSingleObject is made on the semaphore's handle. When +// this function returns a slot has been reserved in the queue for the new +// object. If no slots are available the function blocks until one becomes +// available. Each time an object is removed from the queue ReleaseSemaphore +// is called on the semaphore's handle, thus freeing a slot in the queue. +// If no objects are present in the queue the function blocks until an +// object has been added. + +#define DEFAULT_QUEUESIZE 2 + +template class CQueue { +private: + HANDLE hSemPut; // Semaphore controlling queue "putting" + HANDLE hSemGet; // Semaphore controlling queue "getting" + CRITICAL_SECTION CritSect; // Thread seriallization + int nMax; // Max objects allowed in queue + int iNextPut; // Array index of next "PutMsg" + int iNextGet; // Array index of next "GetMsg" + T *QueueObjects; // Array of objects (ptr's to void) + + void Initialize(int n) { + iNextPut = iNextGet = 0; + nMax = n; + InitializeCriticalSection(&CritSect); + hSemPut = CreateSemaphore(NULL, n, n, NULL); + hSemGet = CreateSemaphore(NULL, 0, n, NULL); + QueueObjects = new T[n]; + } + + +public: + CQueue(int n) { + Initialize(n); + } + + CQueue() { + Initialize(DEFAULT_QUEUESIZE); + } + + ~CQueue() { + delete [] QueueObjects; + DeleteCriticalSection(&CritSect); + CloseHandle(hSemPut); + CloseHandle(hSemGet); + } + + T GetQueueObject() { + int iSlot; + T Object; + LONG lPrevious; + + // Wait for someone to put something on our queue, returns straight + // away is there is already an object on the queue. + // + WaitForSingleObject(hSemGet, INFINITE); + + EnterCriticalSection(&CritSect); + iSlot = iNextGet++ % nMax; + Object = QueueObjects[iSlot]; + LeaveCriticalSection(&CritSect); + + // Release anyone waiting to put an object onto our queue as there + // is now space available in the queue. + // + ReleaseSemaphore(hSemPut, 1L, &lPrevious); + return Object; + } + + void PutQueueObject(T Object) { + int iSlot; + LONG lPrevious; + + // Wait for someone to get something from our queue, returns straight + // away is there is already an empty slot on the queue. + // + WaitForSingleObject(hSemPut, INFINITE); + + EnterCriticalSection(&CritSect); + iSlot = iNextPut++ % nMax; + QueueObjects[iSlot] = Object; + LeaveCriticalSection(&CritSect); + + // Release anyone waiting to remove an object from our queue as there + // is now an object available to be removed. + // + ReleaseSemaphore(hSemGet, 1L, &lPrevious); + } +}; + +// miscellaneous string conversion functions +// NOTE: as we need to use the same binaries on Win95 as on NT this code should +// be compiled WITHOUT unicode being defined. Otherwise we will not pick up +// these internal routines and the binary will not run on Win95. + +int WINAPIV wsprintfWInternal(LPWSTR, LPCWSTR, ...); + +LPWSTR +WINAPI +lstrcpyWInternal( + LPWSTR lpString1, + LPCWSTR lpString2 + ); +LPWSTR +WINAPI +lstrcpynWInternal( + LPWSTR lpString1, + LPCWSTR lpString2, + int iMaxLength + ); +int +WINAPI +lstrcmpWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ); +int +WINAPI +lstrcmpiWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ); +int +WINAPI +lstrlenWInternal( + LPCWSTR lpString + ); + +#ifndef UNICODE +#define wsprintfW wsprintfWInternal +#define lstrcpyW lstrcpyWInternal +#define lstrcpynW lstrcpynWInternal +#define lstrcmpW lstrcmpWInternal +#define lstrcmpiW lstrcmpiWInternal +#define lstrlenW lstrlenWInternal +#endif + +extern "C" +void * __stdcall memmoveInternal(void *, const void *, size_t); + +inline void * __cdecl memchrInternal(const void *buf, int chr, size_t cnt) +{ +#ifdef _X86_ + void *pRet = NULL; + + _asm { + cld // make sure we get the direction right + mov ecx, cnt // num of bytes to scan + mov edi, buf // pointer byte stream + mov eax, chr // byte to scan for + repne scasb // look for the byte in the byte stream + jnz exit_memchr // Z flag set if byte found + dec edi // scasb always increments edi even when it + // finds the required byte + mov pRet, edi +exit_memchr: + } + return pRet; + +#else + while ( cnt && (*(unsigned char *)buf != (unsigned char)chr) ) { + buf = (unsigned char *)buf + 1; + cnt--; + } + + return(cnt ? (void *)buf : NULL); +#endif +} + +void WINAPI IntToWstr(int i, LPWSTR wstr); + +#define WstrToInt(sz) _wtoi(sz) +#define atoiW(sz) _wtoi(sz) +#define atoiA(sz) atoi(sz) + +// These are available to help managing bitmap VIDEOINFOHEADER media structures + +extern const DWORD bits555[3]; +extern const DWORD bits565[3]; +extern const DWORD bits888[3]; + +// These help convert between VIDEOINFOHEADER and BITMAPINFO structures + +STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader); +STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader); +STDAPI_(WORD) GetBitCount(const GUID *pSubtype); + +// strmbase.lib implements this for compatibility with people who +// managed to link to this directly. we don't want to advertise it. +// +// STDAPI_(/* T */ CHAR *) GetSubtypeName(const GUID *pSubtype); + +STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype); +STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype); + +#ifdef UNICODE +#define GetSubtypeName GetSubtypeNameW +#else +#define GetSubtypeName GetSubtypeNameA +#endif + +STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader); +STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader); +STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo); +STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo); + + +// Compares two interfaces and returns TRUE if they are on the same object +BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond); + +// This is for comparing pins +#define EqualPins(pPin1, pPin2) IsEqualObject(pPin1, pPin2) + + +// Arithmetic helper functions + +// Compute (a * b + rnd) / c +LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG rnd); +LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG rnd); + + +// Avoids us dyna-linking to SysAllocString to copy BSTR strings +STDAPI WriteBSTR(BSTR * pstrDest, LPCWSTR szSrc); +STDAPI FreeBSTR(BSTR* pstr); + +// Return a wide string - allocating memory for it +// Returns: +// S_OK - no error +// E_POINTER - ppszReturn == NULL +// E_OUTOFMEMORY - can't allocate memory for returned string +STDAPI AMGetWideString(LPCWSTR pszString, LPWSTR *ppszReturn); + +// Special wait for objects owning windows +DWORD WINAPI WaitDispatchingMessages( + HANDLE hObject, + DWORD dwWait, + HWND hwnd = NULL, + UINT uMsg = 0, + HANDLE hEvent = NULL); + +// HRESULT_FROM_WIN32 converts ERROR_SUCCESS to a success code, but in +// our use of HRESULT_FROM_WIN32, it typically means a function failed +// to call SetLastError(), and we still want a failure code. +// +#define AmHresultFromWin32(x) (MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, x)) + +// call GetLastError and return an HRESULT value that will fail the +// SUCCEEDED() macro. +HRESULT AmGetLastErrorToHResult(void); + +// duplicate of ATL's CComPtr to avoid linker conflicts. + +IUnknown* QzAtlComPtrAssign(IUnknown** pp, IUnknown* lp); + +template +class QzCComPtr +{ +public: + typedef T _PtrClass; + QzCComPtr() {p=NULL;} + QzCComPtr(T* lp) + { + if ((p = lp) != NULL) + p->AddRef(); + } + QzCComPtr(const QzCComPtr& lp) + { + if ((p = lp.p) != NULL) + p->AddRef(); + } + ~QzCComPtr() {if (p) p->Release();} + void Release() {if (p) p->Release(); p=NULL;} + operator T*() {return (T*)p;} + T& operator*() {ASSERT(p!=NULL); return *p; } + //The assert on operator& usually indicates a bug. If this is really + //what is needed, however, take the address of the p member explicitly. + T** operator&() { ASSERT(p==NULL); return &p; } + T* operator->() { ASSERT(p!=NULL); return p; } + T* operator=(T* lp){return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp);} + T* operator=(const QzCComPtr& lp) + { + return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp.p); + } +#if _MSC_VER>1020 + bool operator!(){return (p == NULL);} +#else + BOOL operator!(){return (p == NULL) ? TRUE : FALSE;} +#endif + T* p; +}; + +MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent ); +bool TimeKillSynchronousFlagAvailable( void ); + +#endif /* __WXUTIL__ */ diff --git a/stepmania/src/RageMovieTexture.cpp b/stepmania/src/RageMovieTexture.cpp index f42dfe7f05..33dd0886b4 100644 --- a/stepmania/src/RageMovieTexture.cpp +++ b/stepmania/src/RageMovieTexture.cpp @@ -1,11 +1,12 @@ #include "stdafx.h" /* ----------------------------------------------------------------------------- - File: RageMovieTexture.h + Class: RageMovieTexture - Desc: Based on the DShowTextures example in the DX8 SDK. + Desc: See header. - Copyright (c) 2001 Chris Danford. All rights reserved. + Copyright (c) 2001-2002 by the names listed below. All rights reserved. + Chris Danford ----------------------------------------------------------------------------- */ @@ -18,9 +19,9 @@ // Link with the DirectShow base class libraries #if defined(DEBUG) | defined(_DEBUG) - #pragma comment(lib, "strmbasd.lib") + #pragma comment(lib, "baseclasses/debug/strmbasd.lib") #else - #pragma comment(lib, "strmbase.lib") + #pragma comment(lib, "baseclasses/release/strmbase.lib") #endif //----------------------------------------------------------------------------- diff --git a/stepmania/src/RageMovieTexture.h b/stepmania/src/RageMovieTexture.h index f8220e5f82..72ad3752eb 100644 --- a/stepmania/src/RageMovieTexture.h +++ b/stepmania/src/RageMovieTexture.h @@ -1,10 +1,11 @@ /* ----------------------------------------------------------------------------- - File: RageMovieTexture.h + Class: RageMovieTexture Desc: Based on the DShowTextures example in the DX8 SDK. - Copyright (c) 2001 Chris Danford. All rights reserved. + Copyright (c) 2001-2002 by the names listed below. All rights reserved. + Chris Danford ----------------------------------------------------------------------------- */ @@ -18,7 +19,7 @@ //#include #include -#include +#include "baseclasses/streams.h" //----------------------------------------------------------------------------- // Define GUID for Texture Renderer

SID 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