Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS

Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
This commit is contained in:
Cyd
2026-06-24 21:28:16 -05:00
commit 2b8ca921cb
66341 changed files with 7923174 additions and 0 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+11
View File
@@ -0,0 +1,11 @@
:base graphedt.hlp
:title About the DirectShow Filter Graph Editor
:link graphedt.hlp
:link selfsame
1 About the DirectShow Filter Graph Editor=graphedt_0000000001000000
1 Using the Filter Graph Editor
1 Using the Filter Graph Editor=graphedt_0000000002000000
1 Starting GraphEdit=graphedt_0000000002010000
1 Creating a New Filter Graph=graphedt_0000000002020000
1 Running and Editing a Filter Graph=graphedt_0000000002030000
1 Viewing Properties in GraphEdit=graphedt_0000000002040000
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+102
View File
@@ -0,0 +1,102 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
#include <streams.h> // ActiveMovie base class definitions
#include <mmsystem.h> // Needed for definition of timeGetTime
#include <limits.h> // Standard data type limit definitions
#include <measure.h> // 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)
{
}
// 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)(IBaseFilter *)m_pFilter);
}
}
+51
View File
@@ -0,0 +1,51 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 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);
// Override methods to track end of stream state
STDMETHODIMP EndOfStream();
STDMETHODIMP EndFlush();
HRESULT Active();
HRESULT Run(REFERENCE_TIME tStart);
protected:
// Member variables to track state
BOOL m_bAtEndOfStream; // Set by EndOfStream
BOOL m_bCompleteNotified; // Set when we notify for EC_COMPLETE
private:
void DoCompleteHandling();
};
#endif // __AMEXTRA__
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+248
View File
@@ -0,0 +1,248 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// Helper functions for bitmap formats, March 1995
#include <streams.h>
#include <limits.h>
// 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
const struct {
const GUID *pSubtype;
WORD BitCount;
TCHAR *pName;
} BitCountMap[] = { &MEDIASUBTYPE_RGB1, 1, TEXT("RGB Monochrome"),
&MEDIASUBTYPE_RGB4, 4, TEXT("RGB VGA"),
&MEDIASUBTYPE_RGB8, 8, TEXT("RGB 8"),
&MEDIASUBTYPE_RGB565, 16, TEXT("RGB 565 (16 bit)"),
&MEDIASUBTYPE_RGB555, 16, TEXT("RGB 555 (16 bit)"),
&MEDIASUBTYPE_RGB24, 24, TEXT("RGB 24"),
&MEDIASUBTYPE_RGB32, 32, TEXT("RGB 32"),
&MEDIASUBTYPE_Overlay, 0, TEXT("Overlay"),
&GUID_NULL, 0, TEXT("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
STDAPI_(TCHAR *) GetSubtypeName(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)) {
return BitCountMap[iPosition].pName;
}
if (IsEqualGUID(*pMediaSubtype,GUID_NULL)) {
return TEXT("UNKNOWN");
}
iPosition++;
}
}
// 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);
}
+112
View File
@@ -0,0 +1,112 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// Non MFC based generic cache class, January 1995
/* 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 */
#include <streams.h>
/* Constructor creates an array of pointers (to CBaseObject derived objects)
and then zero fills each position. The cache cannot be resized dynamically
so if the number of objects that you use varies widely during the lifetime
of the cache then there will be some inefficiency. Making the cache resize
would be an expensive operation especially with so little to be gained */
CCache::CCache(TCHAR *pName, INT iItems) :
CBaseObject(pName),
m_iCacheSize(iItems),
m_iUsed(0)
{
ASSERT(iItems > 0);
/* Create the array of pointers and set the cache size, if this does not
succeed then it should probably throw an exception otherwise there is
no way to propogate the error condition back to creating object */
m_ppObjects = (CBaseObject **) new BYTE[iItems * sizeof(CBaseObject *)];
ASSERT(m_ppObjects);
ZeroMemory((PVOID) m_ppObjects,iItems * sizeof(CBaseObject *));
}
/* Destructor causes the cached objects to be deleted (if not already done)
and then deletes the (variable length) array that was used to hold them */
CCache::~CCache()
{
RemoveAll();
delete m_ppObjects;
}
/* Add an item to the cache, if no space is found for it then we return NULL
otherwise we put the object in the free space. The object is meant to be
as small and lightweight as possible so it does not do any multithread
protection. This is assumed to be handled by the owning (list) object */
CBaseObject *CCache::AddToCache(CBaseObject *pObject)
{
/* Have we any room for this object */
if (m_iUsed == m_iCacheSize) {
return NULL;
}
/* Add the object to the end of the cache */
m_ppObjects[m_iUsed] = pObject;
m_iUsed++;
return pObject;
}
/* This is called to retrieve one of the objects we were given to look after
if there are none available then we return NULL otherwise the return the
object. NOTE we do not do any processing on the object so it will be in
exactly the same state when it is handed back as when it is installed */
CBaseObject *CCache::RemoveFromCache()
{
/* Are all the cache positions empty */
if (m_iUsed == 0) {
return NULL;
}
/* Return the last object put into the cache */
m_iUsed--;
ASSERT(m_ppObjects[m_iUsed]);
return m_ppObjects[m_iUsed];
}
/* Delete all the objects held in the cache, simply scans the list of objects
in the cache and deletes each of them in turn. The list class uses a cache
to manage node objects that are frequently created and destroyed and when
the list is finally deleted we will also go at which point we also delete
any remaining node objects. The objects must be derived from CBaseObject */
void CCache::RemoveAll()
{
while (m_iUsed--) {
ASSERT(m_ppObjects[m_iUsed]);
delete m_ppObjects[m_iUsed];
}
}
+78
View File
@@ -0,0 +1,78 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// Non MFC based generic cache class, January 1995
/* 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__ */
+120
View File
@@ -0,0 +1,120 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
CBaseObject
|
|---CGenericList CGuidNameList
|
|---CNode CDisp
|
| INonDelegatingUnknown CMsg CMsgThrd
| |
---CUnknown GUID
| |
| IMediaControl ---FOURCCMap
| |
|---CMediaControl CCache
|
| IMediaPosition MediaType
| | |
|---CMediaPosition CMediaType
| |
| |---CPosPassThru ReferenceTime
| | |
| ---CSourcePosition CRefTime
|
| IBasicAudio CAutoLock
| |
|---CBasicAudio CEvent
|
| IBasicVideo IClassFactory
| | |
|---CBaseBasicVideo CClassFactory
|
| IVideoWindow IClassFactory
| | |
|---CBaseVideoWindow CClassFactory
|
| IFilter CBaseDispatch
| |
| | IAMovieSetup
| | |
|---CBaseFilter CRefTime
| | |
| | IMediaFilter COARefTime
| | |
| |---CBaseMediaFilter
| | |
| | ---CSource
| |
| |
| -------CTransformFilter
| |
| CTransInPlaceFilter
|
| IPin
| |
| | IQualityControl
| | |
|---CBasePin
| |
| |---CBaseOutputPin
| | |
| | |---CImplOutputPin
| | |
| | |---CTransformOutputPin
| | | |
| | | CTransInPlaceOutputPin
| | |
| | | CThread
| | | |
| | CSourceStream
| |
| | IMemInputPin
| | |
| ---CBaseInputPin
| |
| |---CImplInputPin
| |
| ---CTransformInputPin
| |
| CTransInPlaceInputPin
|
| IDispatch
| |
|---CPropertyHelper
|
| IMediaSample
| |
|---CMediaSample
|
| IEnumPins CCritSec
| | |
| | ----------------------|
| | | |
|---CEnumPins |
| |
| IEnumMediaTypes |
| | |
| | ----------------------|
| | | |
|---CEnumMediaTypes |
| |
| IMemAllocator |
| | |
| | ----------------------|
| | | ---COutputQueue
---CBaseAllocator
|
---CMemAllocator
+231
View File
@@ -0,0 +1,231 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// Base class hierachy for creating COM objects, December 1994
#include <streams.h>
#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
m_dwCookie = DbgRegisterObjectCreation(pName);
#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<LPUNKNOWN>( static_cast<PNDUNKNOWN>(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<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )
{
}
#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. */
#ifdef max
#undef max
#endif
template<class T> inline static T max( 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 max(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 max(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);
}
+312
View File
@@ -0,0 +1,312 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// Base class hierachy for creating COM objects, December 1994
/*
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);
~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);
/* 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();
};
/* Return an interface pointer to a requesting client
performing a thread safe AddRef as necessary */
STDAPI GetInterface(LPUNKNOWN pUnk, void **ppv);
/* The standard InterlockedXXX functions won't take volatiles */
static inline LONG WINAPI InterlockedIncrement( volatile LONG * plong )
{ return InterlockedIncrement( const_cast<LONG*>( plong ) ); }
static inline LONG WINAPI InterlockedDecrement( volatile LONG * plong )
{ return InterlockedDecrement( const_cast<LONG*>( plong ) ); }
static inline LONG InterlockedExchange( volatile LONG * plong, LONG new_value )
{ return InterlockedExchange( const_cast<LONG*>( plong ), new_value ); }
/* 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__ */
+372
View File
@@ -0,0 +1,372 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
#include <streams.h>
// 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)
{
}
// 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);
int Length = (lstrlenW(wszTitle) + 1) * sizeof(WCHAR);
// Allocate dynamic memory for the property page title
LPOLESTR pszTitle = (LPOLESTR) QzTaskMemAlloc(Length);
if (pszTitle == NULL) {
NOTE("No caption memory");
return E_OUTOFMEMORY;
}
CopyMemory(pszTitle,wszTitle,Length);
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,(DLGPROC) DialogProc,0L,&pPageInfo->size);
return NOERROR;
}
// Handles the messages for our property window
BOOL CALLBACK CBasePropertyPage::DialogProc(HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
CBasePropertyPage *pPropertyPage;
switch (uMsg) {
case WM_INITDIALOG:
SetWindowLong(hwnd, DWL_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 *) GetWindowLong(hwnd, DWL_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;
}
// Parent should control us so the user can tab out of property page
DWORD dwStyle = GetWindowLong(m_hwnd, GWL_EXSTYLE);
dwStyle = dwStyle | WS_EX_CONTROLPARENT;
SetWindowLong(m_hwnd, GWL_EXSTYLE, dwStyle);
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);
SetWindowLong(m_hwnd, GWL_EXSTYLE, dwStyle);
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
BOOL 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.
//
// control parent bit crashes USER on win9x from graphedt's use of
// property pages.
//
// since these global variables should be zeroed when they are
// faulted in, g_amPlatform shouldn't be uninitialized (and zero
// is VER_PLATFORM_WIN32s). you must call our dll entry point (or
// set this variable yourself).
//
ASSERT(g_amPlatform != 0);
if(g_amPlatform == VER_PLATFORM_WIN32_NT)
{
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);
}
+91
View File
@@ -0,0 +1,91 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 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 BOOL 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
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 BOOL OnReceiveMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
// These implement an IPropertyPage interface
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv);
STDMETHODIMP_(ULONG) NonDelegatingRelease();
STDMETHODIMP_(ULONG) NonDelegatingAddRef();
STDMETHODIMP SetPageSite(LPPROPERTYPAGESITE pPageSite);
STDMETHODIMP Activate(HWND hwndParent,LPCRECT prect,BOOL fModal);
STDMETHODIMP Deactivate(void);
STDMETHODIMP GetPageInfo(LPPROPPAGEINFO pPageInfo);
STDMETHODIMP SetObjects(ULONG cObjects, LPUNKNOWN *ppUnk);
STDMETHODIMP Show(UINT nCmdShow);
STDMETHODIMP Move(LPCRECT prect);
STDMETHODIMP IsPageDirty(void) { return m_bDirty ? S_OK : S_FALSE; }
STDMETHODIMP Apply(void);
STDMETHODIMP Help(LPCWSTR lpszHelpDir) { return E_NOTIMPL; }
STDMETHODIMP TranslateAccelerator(LPMSG lpMsg) { return E_NOTIMPL; }
};
#endif // __CPROP__
File diff suppressed because it is too large Load Diff
+921
View File
@@ -0,0 +1,921 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 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);
~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<CDeferredCommand> m_listPresentation;
// commands queued in stream time are stored here
CGenericList<CDeferredCommand> 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 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__
+133
View File
@@ -0,0 +1,133 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1995 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// File: ddmm.cpp
// Content: Routines for using DirectDraw on a multimonitor system
//--------------------------------------------------------------------------;
#include <streams.h>
#include <ddraw.h>
#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, HANDLE 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;
}
+36
View File
@@ -0,0 +1,36 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1995 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// File: ddmm.h
// Content: Routines for using DirectDraw on a multimonitor system
//--------------------------------------------------------------------------;
#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
#ifndef LPDDENUMCALLBACKEXA
typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKEXA)(GUID FAR *, LPSTR, LPSTR, LPVOID, HANDLE); // wants HMONITOR
typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEXA)( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags);
#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 */
+327
View File
@@ -0,0 +1,327 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
//
// classes used to support dll entrypoints for COM objects.
//
#include <streams.h>
#ifdef DEBUG
#ifdef UNICODE
#ifndef _UNICODE
#define _UNICODE
#endif // _UNICODE
#endif // UNICODE
#include <tchar.h>
#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)
{
switch (ulReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hInstance);
DbgInitialise(hInstance);
{
// The platform identifier is used to work out whether
// full unicode support is available or not. Hence the
// default will be the lowest common denominator - i.e. N/A
g_amPlatform = VER_PLATFORM_WIN32_WINDOWS; // win95 assumed in case GetVersionEx fails
g_osInfo.dwOSVersionInfoSize = sizeof(g_osInfo);
if (GetVersionEx(&g_osInfo)) {
g_amPlatform = g_osInfo.dwPlatformId;
} else {
DbgLog((LOG_ERROR, 1, TEXT("Failed to get the OS platform, assuming Win95")));
}
}
g_hInst = hInstance;
DllInitClasses(TRUE);
break;
case DLL_PROCESS_DETACH:
DllInitClasses(FALSE);
#ifdef DEBUG
if (CBaseObject::ObjectsActive()) {
DbgSetModuleLevel(LOG_MEMORY, 2);
TCHAR szInfo[512];
extern TCHAR m_ModuleName[]; // Cut down module name
TCHAR FullName[_MAX_PATH]; // Load the full path and module name
TCHAR *pName; // Searches from the end for a backslash
GetModuleFileName(NULL,FullName,_MAX_PATH);
pName = _tcsrchr(FullName,'\\');
if (pName == NULL) {
pName = FullName;
} else {
pName++;
}
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;
}
return TRUE;
}
+694
View File
@@ -0,0 +1,694 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
#include <streams.h>
//---------------------------------------------------------------------------
// 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 HRESULT_FROM_WIN32(lreturn);
}
// set description string
//
wsprintf( achTemp, TEXT("%ls"), szDescription );
lreturn = RegSetValue( hkey
, (LPCTSTR)NULL
, REG_SZ
, achTemp
, sizeof(achTemp) );
if( ERROR_SUCCESS != lreturn )
{
RegCloseKey( hkey );
return HRESULT_FROM_WIN32(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 HRESULT_FROM_WIN32(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 HRESULT_FROM_WIN32(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 HRESULT_FROM_WIN32(dwerr);
}
MultiByteToWideChar( CP_ACP
, 0L
, achTemp
, lstrlenA(achTemp) + 1
, achFileName
, sizeof(achFileName) );
}
//
// first registering, register all OLE servers
//
if( bRegister )
{
DbgLog((LOG_TRACE, 2, TEXT("- register OLE Servers")));
hr = RegisterAllServers( achFileName, TRUE );
}
//
// next, register/unregister all filters
//
if( SUCCEEDED(hr) )
{
// init is ref counted so call just in case
// we're being called cold.
//
DbgLog((LOG_TRACE, 2, TEXT("- CoInitialize")));
hr = CoInitialize( (LPVOID)NULL );
ASSERT( SUCCEEDED(hr) );
// get hold of IFilterMapper2
//
DbgLog((LOG_TRACE, 2, TEXT("- obtain IFilterMapper2")));
IFilterMapper2 *pIFM2 = 0;
IFilterMapper *pIFM = 0;
hr = CoCreateInstance( CLSID_FilterMapper2
, NULL
, CLSCTX_INPROC_SERVER
, IID_IFilterMapper2
, (void **)&pIFM2 );
if(FAILED(hr))
{
DbgLog((LOG_TRACE, 2, TEXT("- trying IFilterMapper instead")));
hr = CoCreateInstance(
CLSID_FilterMapper,
NULL,
CLSCTX_INPROC_SERVER,
IID_IFilterMapper,
(void **)&pIFM);
}
if( SUCCEEDED(hr) )
{
// scan through array of CFactoryTemplates
// registering servers and filters.
//
DbgLog((LOG_TRACE, 2, TEXT("- register Filters")));
for( int i = 0; i < g_cTemplates; i++ )
{
// get i'th template
//
const CFactoryTemplate *pT = &g_Templates[i];
if( NULL != pT->m_pAMovieSetup_Filter )
{
DbgLog((LOG_TRACE, 2, TEXT("- - register %ls"), (LPCWSTR)pT->m_Name ));
if(pIFM2)
{
hr = AMovieSetupRegisterFilter2( pT->m_pAMovieSetup_Filter, pIFM2, bRegister );
}
else
{
hr = AMovieSetupRegisterFilter( pT->m_pAMovieSetup_Filter, pIFM, bRegister );
}
}
// check final error for this pass
// and break loop if we failed
//
if( FAILED(hr) )
break;
}
// release interface
//
if(pIFM2)
pIFM2->Release();
else
pIFM->Release();
}
// and clear up
//
CoFreeUnusedLibraries();
CoUninitialize();
}
//
// if unregistering, unregister all OLE servers
//
if( SUCCEEDED(hr) && !bRegister )
{
DbgLog((LOG_TRACE, 2, TEXT("- register OLE Servers")));
hr = RegisterAllServers( achFileName, FALSE );
}
DbgLog((LOG_TRACE, 2, TEXT("- return %0x"), hr));
return hr;
}
//---------------------------------------------------------------------------
//
// AMovieDllRegisterServer()
//
// default ActiveMovie dll setup function
// - to use must be called from an exported
// function named DllRegisterServer()
//
// this function is table driven using the
// static members of the CFactoryTemplate
// class defined in the dll.
//
// it registers the Dll as the InprocServer32
// and then calls the IAMovieSetup.Register
// method.
//
//---------------------------------------------------------------------------
STDAPI
AMovieDllRegisterServer( void )
{
HRESULT hr = NOERROR;
// get file name (where g_hInst is the
// instance handle of the filter dll)
//
WCHAR achFileName[MAX_PATH];
{
// WIN95 doesn't support GetModuleFileNameW
//
char achTemp[MAX_PATH];
if( 0 == GetModuleFileNameA( g_hInst
, achTemp
, sizeof(achTemp) ) )
{
// we've failed!
DWORD dwerr = GetLastError();
return HRESULT_FROM_WIN32(dwerr);
}
MultiByteToWideChar( CP_ACP
, 0L
, achTemp
, lstrlenA(achTemp) + 1
, achFileName
, sizeof(achFileName) );
}
// scan through array of CFactoryTemplates
// registering servers and filters.
//
for( int i = 0; i < g_cTemplates; i++ )
{
// get i'th template
//
const CFactoryTemplate *pT = &g_Templates[i];
// register CLSID and InprocServer32
//
hr = AMovieSetupRegisterServer( *(pT->m_ClsID)
, (LPCWSTR)pT->m_Name
, achFileName );
// instantiate all servers and get hold of
// IAMovieSetup, if implemented, and call
// IAMovieSetup.Register() method
//
if( SUCCEEDED(hr) && (NULL != pT->m_lpfnNew) )
{
// instantiate object
//
PAMOVIESETUP psetup;
hr = CoCreateInstance( *(pT->m_ClsID)
, 0
, CLSCTX_INPROC_SERVER
, IID_IAMovieSetup
, reinterpret_cast<void**>(&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<void**>(&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;
}
+48
View File
@@ -0,0 +1,48 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 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 );
+103
View File
@@ -0,0 +1,103 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 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__ */
+98
View File
@@ -0,0 +1,98 @@
!ifdef NTMAKEENV
!include $(NTMAKEENV)\makefile.def
!else # NTMAKEENV
#===========================================================================;
#
# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
# KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
# PURPOSE.
#
# Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
#
#---------------------------------------------------------------------------;
#
# DirectShow Project Makefile
#
# Targets are provided by DSHOWSDK.MAK
#
# all Create executables (default).
# clean Remove all files created by any goal.
#
#===========================================================================;
# don't place
NO_COPY = 1
# NO_ACTIVEX_DEFAULTS = 1 to disable linking to strmbase.lib, etc.
NO_ACTIVEX_DEFAULTS = 1
NO_COMMON_PCH = 1
# Location relative to root of SDK tree.
!ifndef DXMEDIASDK
DXMEDIASDK = ..\..
!endif
# Location relative to master DSHOWSDK sample makefile.
!ifndef DSHOWSAMPLE
DSHOWSAMPLE = ..\..\samples\multimedia\dshow\src
!endif
# Target
!ifdef NODEBUG
TARGET_NAME = StrmBase
!else
TARGET_NAME = StrmBasD
!endif
TARGET_TYPE = LIBRARY
# Include paths - where to find include files (in
# addition to environment INC path)
INC_PATH = .;$(DXMEDIASDK)\include
# Source files
SRC_FILES = refclock.cpp sysclock.cpp schedule.cpp \
amvideo.cpp ctlutil.cpp amfilter.cpp \
mtype.cpp outputq.cpp source.cpp \
wxutil.cpp transip.cpp wxlist.cpp \
winutil.cpp renbase.cpp winctrl.cpp \
videoctl.cpp pstream.cpp pullpin.cpp \
transfrm.cpp vtrans.cpp \
cprop.cpp wxdebug.cpp combase.cpp \
dllentry.cpp dllsetup.cpp amextra.cpp \
strmctl.cpp ddmm.cpp seekpt.cpp
CODE_LIBS = $(DXMEDIASDK)\lib\strmiids.lib
# Additional tasks
PCH_HEADER = streams.h
PCH_OBJ_NAME = _streams
!ifdef NODEBUG
PCH_NAME = streams
!else
PCH_NAME = streamsd
!endif
END_TASK = copy_files
# include DSHOWSDK.MAK
!include "$(DSHOWSAMPLE)\dshowsdk.mak"
# finally, additional project-specific targets
copy_files:
if exist $(DBG_RTL)\*.lib copy $(DBG_RTL)\*.lib $(DXMEDIASDK)\lib
if exist $(DBG_RTL)\*.pch copy $(DBG_RTL)\*.pch $(DXMEDIASDK)\lib
!endif # NTMAKEENV
@@ -0,0 +1,6 @@
$(ROOT)\sdk\lib\strmbasd.lib: $(ROOT)\lib$(BUILD_ALT_DIR)\$(TARGET_DIRECTORY)\strmbase.lib
copy /v $** $@
$(ROOT)\sdk\lib\strmbase.lib: $(ROOT)\lib$(BUILD_ALT_DIR)\$(TARGET_DIRECTORY)\$(@F)
copy /v $** $@
+224
View File
@@ -0,0 +1,224 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 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
------- <etc. there is a lot of this> ----------------
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__
+125
View File
@@ -0,0 +1,125 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// support for a worker thread class to which you can asynchronously post
// messages
// 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<CMsg> 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;
};
+442
View File
@@ -0,0 +1,442 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// Class that holds and manages media type information, December 1994
// 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 <streams.h>
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)
{
CopyMediaType(this, &rt);
}
CMediaType::CMediaType(const CMediaType& rt)
{
CopyMediaType(this, &rt);
}
// 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)
{
if (&rt != this) {
FreeMediaType(*this);
CopyMediaType(this, &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;
}
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
CopyMediaType(pMediaType,pSrc);
return pMediaType;
}
// Copy 1 media type to another
void 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;
} else {
CopyMemory((PVOID)pmtTarget->pbFormat, (PVOID)pmtSource->pbFormat,
pmtTarget->cbFormat);
}
}
if (pmtTarget->pUnk != NULL) {
pmtTarget->pUnk->AddRef();
}
}
// 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;
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)
+89
View File
@@ -0,0 +1,89 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// Class that holds and manages media type information, December 1994
#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&);
CMediaType(const CMediaType&);
CMediaType& operator=(const CMediaType&);
CMediaType& operator=(const AM_MEDIA_TYPE&);
BOOL operator == (const CMediaType&) const;
BOOL operator != (const CMediaType&) const;
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);
void 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__ */
+770
View File
@@ -0,0 +1,770 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
/*
outputq.cpp
COutputQueue class
This is a 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
*/
#include <streams.h>
//
// 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
) : 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_bTerminate(FALSE),
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 = HRESULT_FROM_WIN32(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 = HRESULT_FROM_WIN32(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;
LONG lNumberToSend; // Local copy
NewSegmentPacket* ppacket;
//
// Get a batch of samples and send it if possible
// In any case exit the loop if there is a control action
// requested
//
{
CAutoLock lck(this);
while (TRUE) {
if (m_bTerminate) {
FreeSamples();
return 0;
}
if (m_bFlushing) {
FreeSamples();
SetEvent(m_evFlushComplete);
}
// Get a sample off the list
pSample = m_List->RemoveHead();
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();
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 secret hack parameter to 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) {
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_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();
if (pSample == NULL) {
break;
}
if (!IsSpecialSample(pSample)) {
pSample->Release();
} else {
if (pSample == NEW_SEGMENT) {
// Free NEW_SEGMENT packet
NewSegmentPacket *ppacket =
(NewSegmentPacket *) m_List->RemoveHead();
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;
}
}
+138
View File
@@ -0,0 +1,138 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
//
// outputq.h
//
// Defines the COutputQueue class
//
// Makes a queue of samples and sends them
// to an output pin
//
// The class will optionally send the samples to the pin directly
//
//
typedef CGenericList<IMediaSample> 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
);
~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();
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)pSample > 0xFFFFFFF0;
};
// 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 *)0xFFFFFFFE) // Send batch
#define EOS_PACKET ((IMediaSample *)0xFFFFFFFD) // End of stream
#define RESET_PACKET ((IMediaSample *)0xFFFFFFFC) // Reset m_hr
#define NEW_SEGMENT ((IMediaSample *)0xFFFFFFFB) // 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;
BOOL m_bFlushed;
// Terminate now
BOOL m_bTerminate;
// Send anyway flag for batching
BOOL m_bSendAnyway;
// Deferred 'return code'
BOOL volatile m_hr;
};
+198
View File
@@ -0,0 +1,198 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
#include <streams.h>
#ifdef PERF
#include <measure.h>
#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)
+115
View File
@@ -0,0 +1,115 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 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__
+529
View File
@@ -0,0 +1,529 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// implementation of CPullPin class - pulls data from IAsyncReader
#include <streams.h>
#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 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 dwUnused;
HRESULT hr = m_pReader->WaitForNext(
0, // no wait
&pSample,
&dwUnused);
if(pSample) {
pSample->Release();
} else {
// no more samples
return;
}
}
}
+154
View File
@@ -0,0 +1,154 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 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__
+332
View File
@@ -0,0 +1,332 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// REFCLOCK.CPP
// Implements the IReferenceClock interface
#include <streams.h>
#include <limits.h>
// '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("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 *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 *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 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 it's a really dumb 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<base 8>(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<CBaseReferenceClock*>(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;
}
+173
View File
@@ -0,0 +1,173 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// REFCLOCK.H
// Implements IReferenceClock Interface
#ifndef __BASEREFCLOCK__
#define __BASEREFCLOCK__
#include <Schedule.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 silly 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 *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 *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 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
+117
View File
@@ -0,0 +1,117 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
//
// CRefTime
//
// Manage reference times.
// Shares same data layout as REFERENCE_TIME, but adds some (nonvirtual)
// functions providing simple comparison, conversion and arithmetic.
//
// A reference time (at the moment) is a unit of seconds represented in
// 100ns units as is used in the Win32 FILETIME structure. BUT the time
// a REFERENCE_TIME represents is NOT the time elapsed since 1/1/1601 it
// will either be stream time or reference time depending upon context
//
// This class provides simple arithmetic operations on reference times
//
// keep non-virtual otherwise the data layout will not be the same as
// REFERENCE_TIME
// -----
// note that you are safe to cast a CRefTime* to a REFERENCE_TIME*, but
// you will need to do so explicitly
// -----
#ifndef __REFTIME__
#define __REFTIME__
const LONGLONG MILLISECONDS = (1000); // 10 ^ 3
const LONGLONG NANOSECONDS = (1000000000); // 10 ^ 9
const LONGLONG UNITS = (NANOSECONDS / 100); // 10 ^ 7
/* Unfortunately an inline function here generates a call to __allmul
- even for constants!
*/
#define MILLISECONDS_TO_100NS_UNITS(lMs) \
Int32x32To64((lMs), (UNITS / MILLISECONDS))
class CRefTime
{
public:
// *MUST* be the only data member so that this class is exactly
// equivalent to a REFERENCE_TIME.
// Also, must be *no virtual functions*
REFERENCE_TIME m_time;
inline CRefTime()
{
// default to 0 time
m_time = 0;
};
inline CRefTime(LONG msecs)
{
m_time = MILLISECONDS_TO_100NS_UNITS(msecs);
};
inline CRefTime(REFERENCE_TIME rt)
{
m_time = rt;
};
inline operator REFERENCE_TIME() const
{
return m_time;
};
inline CRefTime& operator=(const CRefTime& rt)
{
m_time = rt.m_time;
return *this;
};
inline CRefTime& operator=(const LONGLONG ll)
{
m_time = ll;
return *this;
};
inline CRefTime& operator+=(const CRefTime& rt)
{
return (*this = *this + rt);
};
inline CRefTime& operator-=(const CRefTime& rt)
{
return (*this = *this - rt);
};
inline LONG Millisecs(void)
{
return (LONG)(m_time / (UNITS / MILLISECONDS));
};
inline LONGLONG GetUnits(void)
{
return m_time;
};
};
const LONGLONG TimeZero = 0;
#endif /* __REFTIME__ */
File diff suppressed because it is too large Load Diff
+477
View File
@@ -0,0 +1,477 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// Generic ActiveX base renderer class, December 1995
#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 dwUser, // User information
DWORD dw1, // Windows reserved
DWORD 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 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
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__
Binary file not shown.
+297
View File
@@ -0,0 +1,297 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1996 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// SCHEDULE.CPP
#include <streams.h>
// 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 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 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 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))));
m_Serialize.Lock();
#ifdef DEBUG
if (DbgCheckModuleLevel(LOG_TIMING, 4)) DumpLinkedList();
#endif
REFERENCE_TIME rtLate, rtPrevLate = MAX_TIME;
while ( (rtLate = rtTime - (rtNextTime = (pAdvise=head.m_next)->m_rtEventTime)) >= 0 )
{
ASSERT(pAdvise->m_dwAdviseCookie); // If this is zero, its the head or the tail!!
ASSERT( rtLate <= rtPrevLate ); // If we dispatch several, the later ones should
// not be as late as the earlier ones. The ASSERT
// therefore is checking that our sheduling logic
// placed the packet in the right place.
rtPrevLate = rtLate;
rtLate /= 10000;
DbgLog((LOG_TIMING, 2,
TEXT("CAMSchedule::Advise() Dispatching advise %lu for time stamp: %lu ms (%lu ms late)"),
pAdvise->m_dwAdviseCookie, ULONG(pAdvise->m_rtEventTime / (UNITS / MILLISECONDS)), ULONG(rtLate) ));
ASSERT(pAdvise->m_hNotify != INVALID_HANDLE_VALUE);
if (pAdvise->m_bPeriodic == TRUE)
{
EXECUTE_ASSERT(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 ));
m_Serialize.Unlock();
return rtNextTime;
}
/* Private methods */
DWORD 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 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 = %08X"), DWORD(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
+131
View File
@@ -0,0 +1,131 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1996 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// SCHEDULE.H
#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 AddAdvisePacket( const REFERENCE_TIME & time1, const REFERENCE_TIME & time2, HANDLE h, BOOL periodic );
// And a way to cancel
HRESULT Unadvise(DWORD 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 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 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 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 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__
+84
View File
@@ -0,0 +1,84 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
#include <streams.h>
#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("Silly Seeking Thing"),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;
}
+31
View File
@@ -0,0 +1,31 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 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
+497
View File
@@ -0,0 +1,497 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// Implements CSource. A Quartz source filter 'template', March 1995
// 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 <streams.h>
//
// 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);
}
//
// 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 bogus
// 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; i<m_iPins; ++i) {
if ((IPin *)(m_paStreams[i])==iPin) {
return i;
}
}
return -1;
}
//
// GetPinCount
//
// Returns the number of pins this filter has
int CSource::GetPinCount(void) {
CAutoLock lock(&m_cStateLock);
return m_iPins;
}
//
// GetPin
//
// Return a non-addref'd pointer to pin n
// needed by CBaseFilter
CBasePin *CSource::GetPin(int n) {
CAutoLock lock(&m_cStateLock);
// n must be in the range 0..m_iPins-1
// if m_iPins>n && 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 a bogus 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);
}
//
// 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;
}
+166
View File
@@ -0,0 +1,166 @@
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
// Classes to simplify creation of ActiveX source filters that support
// continuous generation of data. It provides no support for IMediaControl
// or IMediaPosition
//
// 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);
~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);
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__
+59
View File
@@ -0,0 +1,59 @@
ROOT=$(MAKEDIR:\sdk\classes\base=)
!include $(ROOT)\common.inc
!if $(386) && $(FREEBUILD)
!undef USE_MSVCRT
!endif
INCLUDES=.;..\..\include
TARGETNAME=strmbase
TARGETPATH=$(ROOT)\lib$(BUILD_ALT_DIR)
TARGETTYPE=LIBRARY
SOURCES= \
amextra.cpp \
amvideo.cpp \
combase.cpp \
cprop.cpp \
ctlutil.cpp \
dllentry.cpp \
dllsetup.cpp \
amfilter.cpp \
mtype.cpp \
outputq.cpp \
pstream.cpp \
pullpin.cpp \
refclock.cpp \
renbase.cpp \
schedule.cpp \
seekpt.cpp \
source.cpp \
strmctl.cpp \
sysclock.cpp \
transfrm.cpp \
transip.cpp \
videoctl.cpp \
vtrans.cpp \
winctrl.cpp \
winutil.cpp \
wxdebug.cpp \
wxlist.cpp \
wxutil.cpp \
ddmm.cpp
PRECOMPILED_INCLUDE=streams.h
PRECOMPILED_PCH=streams.pch
PRECOMPILED_OBJ=streams.obj
PRECOMPILED_CXX=1
OBJLIBFILES=$(SDK_LIB_PATH)\strmiids.lib
!if $(FREEBUILD)
NTTARGETFILES=$(ROOT)\sdk\lib\strmbase.lib
!else
NTTARGETFILES=$(ROOT)\sdk\lib\strmbasd.lib
!endif
SYNCHRONIZE_BLOCK=1

Some files were not shown because too many files have changed in this diff Show More