mirror of
https://github.com/libretro/pcsx2.git
synced 2025-01-01 14:58:22 +00:00
gsdx-legacy: Remove baseclasses
Use the baseclasses project in unfree instead.
This commit is contained in:
parent
4768f912b8
commit
471722482e
plugins/GSdx_legacy
GSdx.vcxprojGSdx.vcxproj.filters
baseclasses
activex.rcvactivex.veramextra.cppamextra.hamfilter.cppamfilter.hamvideo.cppcache.hcombase.cppcombase.hctlutil.cppctlutil.hddmm.cppddmm.hdsschedule.hfourcc.hmeasure.hmsgthrd.hmtype.cppmtype.houtputq.cppoutputq.hpstream.cpppstream.hpullpin.cpppullpin.hrefclock.cpprefclock.hreftime.hrenbase.cpprenbase.hschedule.cppschedule.hseekpt.cppseekpt.hsource.cppsource.hstreams.hstrmctl.cppstrmctl.hsysclock.cppsysclock.htransfrm.cpptransfrm.htransip.cpptransip.hvtrans.cppvtrans.hwxdebug.cppwxdebug.hwxlist.cppwxlist.hwxutil.cppwxutil.h
vsprops
File diff suppressed because it is too large
Load Diff
@ -16,9 +16,6 @@
|
||||
<Filter Include="Shaders">
|
||||
<UniqueIdentifier>{6d029896-e5fd-4b46-8576-52d7d90125e6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Baseclasses">
|
||||
<UniqueIdentifier>{3c2d6a4a-ff5a-420d-a0f7-4c17cc5c19df}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Xbyak">
|
||||
<UniqueIdentifier>{d6fcc23b-bc82-4390-8a9a-928910bc4123}</UniqueIdentifier>
|
||||
</Filter>
|
||||
@ -204,75 +201,6 @@
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\amextra.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\amfilter.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\amvideo.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\combase.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\ctlutil.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\ddmm.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\mtype.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\outputq.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\pstream.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\pullpin.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\refclock.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\renbase.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\schedule.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\seekpt.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\source.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\strmctl.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\sysclock.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\transfrm.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\transip.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\vtrans.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\wxdebug.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\wxlist.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="baseclasses\wxutil.cpp">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GLLoader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -566,93 +494,6 @@
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\amextra.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\amfilter.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\cache.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\combase.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\ctlutil.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\ddmm.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\dsschedule.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\fourcc.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\measure.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\msgthrd.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\mtype.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\outputq.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\pstream.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\pullpin.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\refclock.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\reftime.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\renbase.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\schedule.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\seekpt.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\source.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\streams.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\strmctl.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\sysclock.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\transfrm.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\transip.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\vtrans.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\wxdebug.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\wxlist.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baseclasses\wxutil.h">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="xbyak\xbyak.h">
|
||||
<Filter>Xbyak</Filter>
|
||||
</ClInclude>
|
||||
@ -742,12 +583,6 @@
|
||||
<None Include="res\tfx.fx">
|
||||
<Filter>Shaders</Filter>
|
||||
</None>
|
||||
<None Include="baseclasses\activex.rcv">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</None>
|
||||
<None Include="baseclasses\activex.ver">
|
||||
<Filter>Baseclasses</Filter>
|
||||
</None>
|
||||
<None Include="GSdx.def" />
|
||||
<None Include="res\fxaa.fx">
|
||||
<Filter>Shaders</Filter>
|
||||
|
@ -1,142 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Activex.rcv
|
||||
//
|
||||
// Desc: DirectShow base classes - this file defines the version resource
|
||||
// used for the application.
|
||||
//
|
||||
// NOTE: All strings MUST have an explicit \0 for termination!
|
||||
//
|
||||
// For a complete description of the Version Resource, search the
|
||||
// Microsoft Developer's Network (MSDN) CD-ROM for 'version resource'..
|
||||
//
|
||||
// Copyright (c) 1992 - 2002, Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef _ACTIVEX_RCV_
|
||||
#define _ACTIVEX_RCV_
|
||||
|
||||
#ifndef WIN32
|
||||
#define WIN32
|
||||
#endif
|
||||
#include <winver.h>
|
||||
|
||||
#ifndef _ACTIVEX_VER_
|
||||
#include <activex.ver>
|
||||
#endif
|
||||
|
||||
//
|
||||
// Version flags.
|
||||
//
|
||||
// OFFICIAL and FINAL should be defined when appropriate.
|
||||
//
|
||||
|
||||
#ifndef OFFICIAL
|
||||
#define VER_PRIVATEBUILD VS_FF_PRIVATEBUILD
|
||||
#else
|
||||
#define VER_PRIVATEBUILD 0
|
||||
#endif
|
||||
|
||||
#ifndef FINAL
|
||||
#define VER_PRERELEASE VS_FF_PRERELEASE
|
||||
#else
|
||||
#define VER_PRERELEASE 0
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#define VER_DEBUG VS_FF_DEBUG
|
||||
#else
|
||||
#define VER_DEBUG 0
|
||||
#endif
|
||||
|
||||
//
|
||||
// Version definitions
|
||||
//
|
||||
|
||||
#define VERSION_RES_FLAGSMASK 0x0030003FL
|
||||
#define VERSION_RES_FLAGS (VER_PRIVATEBUILD|VER_PRERELEASE|VER_DEBUG)
|
||||
|
||||
#ifndef VERSION_RES_OS
|
||||
#define VERSION_RES_OS VOS__WINDOWS32
|
||||
#endif
|
||||
|
||||
#ifndef VERSION_RES_TYPE
|
||||
#define VERSION_RES_TYPE VFT_DLL
|
||||
#endif
|
||||
|
||||
#ifndef VERSION_RES_SUBTYPE
|
||||
#define VERSION_RES_SUBTYPE VFT2_UNKNOWN
|
||||
#endif
|
||||
|
||||
#define VERSION_RES_LANGUAGE 0x409
|
||||
|
||||
#ifndef VERSION_RES_CHARSET
|
||||
#ifdef UNICODE
|
||||
#define VERSION_RES_CHARSET 1200
|
||||
#else
|
||||
#define VERSION_RES_CHARSET 1252
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef VERSION_RES_ACTIVEX
|
||||
#define VERSION_RES_ACTIVEX "Filter dll\0"
|
||||
#endif
|
||||
|
||||
#ifdef AMOVIE_SELF_REGISTER
|
||||
#ifndef OLE_SELF_REGISTER
|
||||
#define OLE_SELF_REGISTER
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef OLE_SELF_REGISTER
|
||||
#ifdef AMOVIE_SELF_REGISTER
|
||||
#define VERSION_RES_SELFREGISTER "AM20\0"
|
||||
#else
|
||||
#define VERSION_RES_SELFREGISTER "\0"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//
|
||||
// Version resource
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION VERSION_RES_MAJOR_VER, VERSION_RES_MINOR_VER, 0, VERSION_RES_BUILD
|
||||
PRODUCTVERSION VERSION_RES_MAJOR_VER, VERSION_RES_MINOR_VER, 0, VERSION_RES_BUILD
|
||||
FILEFLAGSMASK VERSION_RES_FLAGSMASK
|
||||
FILEFLAGS VERSION_RES_FLAGS
|
||||
FILEOS VERSION_RES_OS
|
||||
FILETYPE VERSION_RES_TYPE
|
||||
FILESUBTYPE VERSION_RES_SUBTYPE
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904E4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", VERSION_RES_COMPANY_NAME
|
||||
VALUE "Comment", VERSION_RES_COMMENT
|
||||
VALUE "FileDescription", VERSION_RES_BIN_DESCRIPTION
|
||||
VALUE "FileVersion", VERSION_RES_STRING
|
||||
VALUE "InternalName", VERSION_RES_BIN_NAME
|
||||
VALUE "LegalCopyright", VERSION_RES_COPYRIGHT
|
||||
VALUE "OriginalFilename", VERSION_RES_BIN_NAME
|
||||
VALUE "ProductName", VERSION_RES_PRODUCT_NAME
|
||||
#ifdef DEBUG
|
||||
VALUE "ProductVersion", VERSION_RES_STRING_D
|
||||
#else
|
||||
VALUE "ProductVersion", VERSION_RES_STRING
|
||||
#endif
|
||||
VALUE "ActiveMovie", VERSION_RES_ACTIVEX
|
||||
#ifdef OLE_SELF_REGISTER
|
||||
VALUE "OLESelfRegister", VERSION_RES_SELFREGISTER
|
||||
#endif
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", VERSION_RES_LANGUAGE, VERSION_RES_CHARSET
|
||||
END
|
||||
END
|
||||
|
||||
#endif
|
||||
// _ACTIVEX_RCV_
|
@ -1,56 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Activex.ver
|
||||
//
|
||||
// Desc: DirectShow base classes - common versioning information for
|
||||
// ACTIVEX binaries.
|
||||
//
|
||||
// Copyright (c) 1996-2002, Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef _ACTIVEX_VER_
|
||||
#define _ACTIVEX_VER_
|
||||
|
||||
// NOTE: all string resources that will be used in ACTIVEX.RCV for the
|
||||
// version resource information *MUST* have an explicit \0 terminator!
|
||||
|
||||
#define VERSION_RES_MAJOR_VER 9
|
||||
#define VERSION_RES_MINOR_VER 0
|
||||
#define VERSION_RES_BUILD 0
|
||||
|
||||
#define VERSION_RES_STRING_D "9.00 (Debug)\0"
|
||||
#define VERSION_RES_STRING "9.00\0"
|
||||
|
||||
#define VERSION_RES_PRODUCT_NAME "DirectX 9.0 Sample\0"
|
||||
#define VERSION_RES_COMMENT "DirectShow Sample\0"
|
||||
#define VERSION_RES_COMPANY_NAME "Microsoft Corporation\0"
|
||||
#define VERSION_RES_COPYRIGHT "Copyright (C) 1992-2002 Microsoft Corporation\0"
|
||||
|
||||
// The following defines are required on a file-by-file basis
|
||||
//
|
||||
// #define VERSION_RES_BIN_NAME "sample.ax\0"
|
||||
// #define VERSION_RES_BIN_DESCRIPTION "Sample Filter\0"
|
||||
//
|
||||
// Also required, if you don't want the defaults, are
|
||||
//
|
||||
// #define VERSION_RES_ACTIVEX "Filter dll\0" (the default value)
|
||||
//
|
||||
// A string defining the type of component.
|
||||
//
|
||||
// #define VERSION_RES_TYPE VFT_DLL (default)
|
||||
// VFT_APP
|
||||
// VFT_VXD
|
||||
// VFT_DRV
|
||||
// VFT_FONT
|
||||
// VFT_STATIC_LIB
|
||||
// VFT_UNKNOWN
|
||||
//
|
||||
// #define VERSION_RES_SUBTYPE VFT2_UNKNOWN (default)
|
||||
// VFT2_DRV_INSTALLABLE
|
||||
// VFT2_DRV_SOUND
|
||||
// <several other options>
|
||||
//
|
||||
// See winver.h for further details
|
||||
|
||||
#endif
|
||||
|
@ -1,111 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: AMExtra.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements CRenderedInputPin class.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include "streams.h" // DirectShow 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)
|
||||
{
|
||||
}
|
||||
#ifdef UNICODE
|
||||
CRenderedInputPin::CRenderedInputPin(CHAR *pObjectName,
|
||||
CBaseFilter *pFilter,
|
||||
CCritSec *pLock,
|
||||
HRESULT *phr,
|
||||
LPCWSTR pName) :
|
||||
CBaseInputPin(pObjectName, pFilter, pLock, phr, pName),
|
||||
m_bAtEndOfStream(FALSE),
|
||||
m_bCompleteNotified(FALSE)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
// Flush end of stream condition - caller should do any
|
||||
// necessary stream level locking before calling this
|
||||
|
||||
STDMETHODIMP CRenderedInputPin::EndOfStream()
|
||||
{
|
||||
HRESULT hr = CheckStreaming();
|
||||
|
||||
// Do EC_COMPLETE handling for rendered pins
|
||||
if (S_OK == hr && !m_bAtEndOfStream) {
|
||||
m_bAtEndOfStream = TRUE;
|
||||
FILTER_STATE fs;
|
||||
EXECUTE_ASSERT(SUCCEEDED(m_pFilter->GetState(0, &fs)));
|
||||
if (fs == State_Running) {
|
||||
DoCompleteHandling();
|
||||
}
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
// Called to complete the flush
|
||||
|
||||
STDMETHODIMP CRenderedInputPin::EndFlush()
|
||||
{
|
||||
CAutoLock lck(m_pLock);
|
||||
|
||||
// Clean up renderer state
|
||||
m_bAtEndOfStream = FALSE;
|
||||
m_bCompleteNotified = FALSE;
|
||||
|
||||
return CBaseInputPin::EndFlush();
|
||||
}
|
||||
|
||||
|
||||
// Notify of Run() from filter
|
||||
|
||||
HRESULT CRenderedInputPin::Run(REFERENCE_TIME tStart)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(tStart);
|
||||
m_bCompleteNotified = FALSE;
|
||||
if (m_bAtEndOfStream) {
|
||||
DoCompleteHandling();
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
// Clear status on going into paused state
|
||||
|
||||
HRESULT CRenderedInputPin::Active()
|
||||
{
|
||||
m_bAtEndOfStream = FALSE;
|
||||
m_bCompleteNotified = FALSE;
|
||||
return CBaseInputPin::Active();
|
||||
}
|
||||
|
||||
|
||||
// Do stuff to deliver end of stream
|
||||
|
||||
void CRenderedInputPin::DoCompleteHandling()
|
||||
{
|
||||
ASSERT(m_bAtEndOfStream);
|
||||
if (!m_bCompleteNotified) {
|
||||
m_bCompleteNotified = TRUE;
|
||||
m_pFilter->NotifyEvent(EC_COMPLETE, S_OK, (LONG_PTR)(IBaseFilter *)m_pFilter);
|
||||
}
|
||||
}
|
||||
|
@ -1,56 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: AMExtra.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __AMEXTRA__
|
||||
#define __AMEXTRA__
|
||||
|
||||
// Simple rendered input pin
|
||||
//
|
||||
// NOTE if your filter queues stuff before rendering then it may not be
|
||||
// appropriate to use this class
|
||||
//
|
||||
// In that case queue the end of stream condition until the last sample
|
||||
// is actually rendered and flush the condition appropriately
|
||||
|
||||
class CRenderedInputPin : public CBaseInputPin
|
||||
{
|
||||
public:
|
||||
|
||||
CRenderedInputPin(TCHAR *pObjectName,
|
||||
CBaseFilter *pFilter,
|
||||
CCritSec *pLock,
|
||||
HRESULT *phr,
|
||||
LPCWSTR pName);
|
||||
#ifdef UNICODE
|
||||
CRenderedInputPin(CHAR *pObjectName,
|
||||
CBaseFilter *pFilter,
|
||||
CCritSec *pLock,
|
||||
HRESULT *phr,
|
||||
LPCWSTR pName);
|
||||
#endif
|
||||
|
||||
// Override methods to track end of stream state
|
||||
STDMETHODIMP EndOfStream();
|
||||
STDMETHODIMP EndFlush();
|
||||
|
||||
HRESULT Active();
|
||||
HRESULT Run(REFERENCE_TIME tStart);
|
||||
|
||||
protected:
|
||||
|
||||
// Member variables to track state
|
||||
BOOL m_bAtEndOfStream; // Set by EndOfStream
|
||||
BOOL m_bCompleteNotified; // Set when we notify for EC_COMPLETE
|
||||
|
||||
private:
|
||||
void DoCompleteHandling();
|
||||
};
|
||||
|
||||
#endif // __AMEXTRA__
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,275 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: AMVideo.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements helper functions for
|
||||
// bitmap formats.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#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. unicode and ansi versions are stored because we have to
|
||||
// return a pointer to a static string.
|
||||
const struct {
|
||||
const GUID *pSubtype;
|
||||
WORD BitCount;
|
||||
CHAR *pName;
|
||||
WCHAR *wszName;
|
||||
} BitCountMap[] = { &MEDIASUBTYPE_RGB1, 1, "RGB Monochrome", L"RGB Monochrome",
|
||||
&MEDIASUBTYPE_RGB4, 4, "RGB VGA", L"RGB VGA",
|
||||
&MEDIASUBTYPE_RGB8, 8, "RGB 8", L"RGB 8",
|
||||
&MEDIASUBTYPE_RGB565, 16, "RGB 565 (16 bit)", L"RGB 565 (16 bit)",
|
||||
&MEDIASUBTYPE_RGB555, 16, "RGB 555 (16 bit)", L"RGB 555 (16 bit)",
|
||||
&MEDIASUBTYPE_RGB24, 24, "RGB 24", L"RGB 24",
|
||||
&MEDIASUBTYPE_RGB32, 32, "RGB 32", L"RGB 32",
|
||||
&MEDIASUBTYPE_ARGB32, 32, "ARGB 32", L"ARGB 32",
|
||||
&MEDIASUBTYPE_Overlay, 0, "Overlay", L"Overlay",
|
||||
&GUID_NULL, 0, "UNKNOWN", L"UNKNOWN"
|
||||
};
|
||||
|
||||
// Return the size of the bitmap as defined by this header
|
||||
|
||||
STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader)
|
||||
{
|
||||
return DIBSIZE(*pHeader);
|
||||
}
|
||||
|
||||
|
||||
// This is called if the header has a 16 bit colour depth and needs to work
|
||||
// out the detailed type from the bit fields (either RGB 565 or RGB 555)
|
||||
|
||||
STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader)
|
||||
{
|
||||
BITMAPINFO *pbmInfo = (BITMAPINFO *) pbmiHeader;
|
||||
ASSERT(pbmiHeader->biBitCount == 16);
|
||||
|
||||
// If its BI_RGB then it's RGB 555 by default
|
||||
|
||||
if (pbmiHeader->biCompression == BI_RGB) {
|
||||
return MEDIASUBTYPE_RGB555;
|
||||
}
|
||||
|
||||
// Compare the bit fields with RGB 555
|
||||
|
||||
DWORD *pMask = (DWORD *) pbmInfo->bmiColors;
|
||||
if (pMask[0] == bits555[0]) {
|
||||
if (pMask[1] == bits555[1]) {
|
||||
if (pMask[2] == bits555[2]) {
|
||||
return MEDIASUBTYPE_RGB555;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compare the bit fields with RGB 565
|
||||
|
||||
pMask = (DWORD *) pbmInfo->bmiColors;
|
||||
if (pMask[0] == bits565[0]) {
|
||||
if (pMask[1] == bits565[1]) {
|
||||
if (pMask[2] == bits565[2]) {
|
||||
return MEDIASUBTYPE_RGB565;
|
||||
}
|
||||
}
|
||||
}
|
||||
return GUID_NULL;
|
||||
}
|
||||
|
||||
|
||||
// Given a BITMAPINFOHEADER structure this returns the GUID sub type that is
|
||||
// used to describe it in format negotiations. For example a video codec fills
|
||||
// in the format block with a VIDEOINFO structure, it also fills in the major
|
||||
// type with MEDIATYPE_VIDEO and the subtype with a GUID that matches the bit
|
||||
// count, for example if it is an eight bit image then MEDIASUBTYPE_RGB8
|
||||
|
||||
STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader)
|
||||
{
|
||||
ASSERT(pbmiHeader);
|
||||
|
||||
// If it's not RGB then create a GUID from the compression type
|
||||
|
||||
if (pbmiHeader->biCompression != BI_RGB) {
|
||||
if (pbmiHeader->biCompression != BI_BITFIELDS) {
|
||||
FOURCCMap FourCCMap(pbmiHeader->biCompression);
|
||||
return (const GUID) FourCCMap;
|
||||
}
|
||||
}
|
||||
|
||||
// Map the RGB DIB bit depth to a image GUID
|
||||
|
||||
switch(pbmiHeader->biBitCount) {
|
||||
case 1 : return MEDIASUBTYPE_RGB1;
|
||||
case 4 : return MEDIASUBTYPE_RGB4;
|
||||
case 8 : return MEDIASUBTYPE_RGB8;
|
||||
case 16 : return GetTrueColorType(pbmiHeader);
|
||||
case 24 : return MEDIASUBTYPE_RGB24;
|
||||
case 32 : return MEDIASUBTYPE_RGB32;
|
||||
}
|
||||
return GUID_NULL;
|
||||
}
|
||||
|
||||
|
||||
// Given a video bitmap subtype we return the number of bits per pixel it uses
|
||||
// We return a WORD bit count as thats what the BITMAPINFOHEADER uses. If the
|
||||
// GUID subtype is not found in the table we return an invalid USHRT_MAX
|
||||
|
||||
STDAPI_(WORD) GetBitCount(const GUID *pSubtype)
|
||||
{
|
||||
ASSERT(pSubtype);
|
||||
const GUID *pMediaSubtype;
|
||||
INT iPosition = 0;
|
||||
|
||||
// Scan the mapping list seeing if the source GUID matches any known
|
||||
// bitmap subtypes, the list is terminated by a GUID_NULL entry
|
||||
|
||||
while (TRUE) {
|
||||
pMediaSubtype = BitCountMap[iPosition].pSubtype;
|
||||
if (IsEqualGUID(*pMediaSubtype,GUID_NULL)) {
|
||||
return USHRT_MAX;
|
||||
}
|
||||
if (IsEqualGUID(*pMediaSubtype,*pSubtype)) {
|
||||
return BitCountMap[iPosition].BitCount;
|
||||
}
|
||||
iPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Given a bitmap subtype we return a description name that can be used for
|
||||
// debug purposes. In a retail build this function still returns the names
|
||||
// If the subtype isn't found in the lookup table we return string UNKNOWN
|
||||
|
||||
int LocateSubtype(const GUID *pSubtype)
|
||||
{
|
||||
ASSERT(pSubtype);
|
||||
const GUID *pMediaSubtype;
|
||||
INT iPosition = 0;
|
||||
|
||||
// Scan the mapping list seeing if the source GUID matches any known
|
||||
// bitmap subtypes, the list is terminated by a GUID_NULL entry
|
||||
|
||||
while (TRUE) {
|
||||
pMediaSubtype = BitCountMap[iPosition].pSubtype;
|
||||
if (IsEqualGUID(*pMediaSubtype,*pSubtype) ||
|
||||
IsEqualGUID(*pMediaSubtype,GUID_NULL)
|
||||
)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
iPosition++;
|
||||
}
|
||||
|
||||
return iPosition;
|
||||
}
|
||||
|
||||
|
||||
|
||||
STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype)
|
||||
{
|
||||
return BitCountMap[LocateSubtype(pSubtype)].wszName;
|
||||
}
|
||||
|
||||
STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype)
|
||||
{
|
||||
return BitCountMap[LocateSubtype(pSubtype)].pName;
|
||||
}
|
||||
|
||||
#ifndef GetSubtypeName
|
||||
#error wxutil.h should have defined GetSubtypeName
|
||||
#endif
|
||||
#undef GetSubtypeName
|
||||
|
||||
// this is here for people that linked to it directly; most people
|
||||
// would use the header file that picks the A or W version.
|
||||
STDAPI_(CHAR *) GetSubtypeName(const GUID *pSubtype)
|
||||
{
|
||||
return GetSubtypeNameA(pSubtype);
|
||||
}
|
||||
|
||||
|
||||
// The mechanism for describing a bitmap format is with the BITMAPINFOHEADER
|
||||
// This is really messy to deal with because it invariably has fields that
|
||||
// follow it holding bit fields, palettes and the rest. This function gives
|
||||
// the number of bytes required to hold a VIDEOINFO that represents it. This
|
||||
// count includes the prefix information (like the rcSource rectangle) the
|
||||
// BITMAPINFOHEADER field, and any other colour information on the end.
|
||||
//
|
||||
// WARNING If you want to copy a BITMAPINFOHEADER into a VIDEOINFO always make
|
||||
// sure that you use the HEADER macro because the BITMAPINFOHEADER field isn't
|
||||
// right at the start of the VIDEOINFO (there are a number of other fields),
|
||||
//
|
||||
// CopyMemory(HEADER(pVideoInfo),pbmi,sizeof(BITMAPINFOHEADER));
|
||||
//
|
||||
|
||||
STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader)
|
||||
{
|
||||
// Everyone has this to start with this
|
||||
LONG Size = SIZE_PREHEADER + pHeader->biSize;
|
||||
|
||||
ASSERT(pHeader->biSize >= sizeof(BITMAPINFOHEADER));
|
||||
|
||||
// Does this format use a palette, if the number of colours actually used
|
||||
// is zero then it is set to the maximum that are allowed for that colour
|
||||
// depth (an example is 256 for eight bits). Truecolour formats may also
|
||||
// pass a palette with them in which case the used count is non zero
|
||||
|
||||
// This would scare me.
|
||||
ASSERT(pHeader->biBitCount <= iPALETTE || pHeader->biClrUsed == 0);
|
||||
|
||||
if (pHeader->biBitCount <= iPALETTE || pHeader->biClrUsed) {
|
||||
LONG Entries = (DWORD) 1 << pHeader->biBitCount;
|
||||
if (pHeader->biClrUsed) {
|
||||
Entries = pHeader->biClrUsed;
|
||||
}
|
||||
Size += Entries * sizeof(RGBQUAD);
|
||||
}
|
||||
|
||||
// Truecolour formats may have a BI_BITFIELDS specifier for compression
|
||||
// type which means that room for three DWORDs should be allocated that
|
||||
// specify where in each pixel the RGB colour components may be found
|
||||
|
||||
if (pHeader->biCompression == BI_BITFIELDS) {
|
||||
Size += SIZE_MASKS;
|
||||
}
|
||||
|
||||
// A BITMAPINFO for a palettised image may also contain a palette map that
|
||||
// provides the information to map from a source palette to a destination
|
||||
// palette during a BitBlt for example, because this information is only
|
||||
// ever processed during drawing you don't normally store the palette map
|
||||
// nor have any way of knowing if it is present in the data structure
|
||||
|
||||
return Size;
|
||||
}
|
||||
|
||||
|
||||
// Returns TRUE if the VIDEOINFO contains a palette
|
||||
|
||||
STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo)
|
||||
{
|
||||
if (PALETTISED(pVideoInfo) == FALSE) {
|
||||
if (pVideoInfo->bmiHeader.biClrUsed == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Return a pointer to the first entry in a palette
|
||||
|
||||
STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo)
|
||||
{
|
||||
if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) {
|
||||
return TRUECOLOR(pVideoInfo)->bmiColors;
|
||||
}
|
||||
return COLORS(pVideoInfo);
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Cache.h
|
||||
//
|
||||
// Desc: DirectShow base classes - efines a non-MFC generic cache class.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/* This class implements a simple cache. A cache object is instantiated
|
||||
with the number of items it is to hold. An item is a pointer to an
|
||||
object derived from CBaseObject (helps reduce memory leaks). The cache
|
||||
can then have objects added to it and removed from it. The cache size
|
||||
is fixed at construction time and may therefore run out or be flooded.
|
||||
If it runs out it returns a NULL pointer, if it fills up it also returns
|
||||
a NULL pointer instead of a pointer to the object just inserted */
|
||||
|
||||
/* Making these classes inherit from CBaseObject does nothing for their
|
||||
functionality but it allows us to check there are no memory leaks */
|
||||
|
||||
/* WARNING Be very careful when using this class, what it lets you do is
|
||||
store and retrieve objects so that you can minimise object creation
|
||||
which in turns improves efficiency. However the object you store is
|
||||
exactly the same as the object you get back which means that it short
|
||||
circuits the constructor initialisation phase. This means any class
|
||||
variables the object has (eg pointers) are highly likely to be invalid.
|
||||
Therefore ensure you reinitialise the object before using it again */
|
||||
|
||||
|
||||
#ifndef __CACHE__
|
||||
#define __CACHE__
|
||||
|
||||
|
||||
class CCache : CBaseObject {
|
||||
|
||||
/* Make copy constructor and assignment operator inaccessible */
|
||||
|
||||
CCache(const CCache &refCache);
|
||||
CCache &operator=(const CCache &refCache);
|
||||
|
||||
private:
|
||||
|
||||
/* These are initialised in the constructor. The first variable points to
|
||||
an array of pointers, each of which points to a CBaseObject derived
|
||||
object. The m_iCacheSize is the static fixed size for the cache and the
|
||||
m_iUsed defines the number of places filled with objects at any time.
|
||||
We fill the array of pointers from the start (ie m_ppObjects[0] first)
|
||||
and then only add and remove objects from the end position, so in this
|
||||
respect the array of object pointers should be treated as a stack */
|
||||
|
||||
CBaseObject **m_ppObjects;
|
||||
const INT m_iCacheSize;
|
||||
INT m_iUsed;
|
||||
|
||||
public:
|
||||
|
||||
CCache(TCHAR *pName,INT iItems);
|
||||
virtual ~CCache();
|
||||
|
||||
/* Add an item to the cache */
|
||||
CBaseObject *AddToCache(CBaseObject *pObject);
|
||||
|
||||
/* Remove an item from the cache */
|
||||
CBaseObject *RemoveFromCache();
|
||||
|
||||
/* Delete all the objects held in the cache */
|
||||
void RemoveAll(void);
|
||||
|
||||
/* Return the cache size which is set during construction */
|
||||
INT GetCacheSize(void) const {return m_iCacheSize;};
|
||||
};
|
||||
|
||||
#endif /* __CACHE__ */
|
||||
|
@ -1,256 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: ComBase.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements class hierarchy for creating
|
||||
// COM objects.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#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
|
||||
|
||||
#ifdef UNICODE
|
||||
m_dwCookie = DbgRegisterObjectCreation(0, pName);
|
||||
#else
|
||||
m_dwCookie = DbgRegisterObjectCreation(pName, 0);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
CBaseObject::CBaseObject(const char *pName)
|
||||
{
|
||||
/* Increment the number of active objects */
|
||||
InterlockedIncrement(&m_cObjects);
|
||||
|
||||
#ifdef DEBUG
|
||||
m_dwCookie = DbgRegisterObjectCreation(pName, 0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
HINSTANCE hlibOLEAut32;
|
||||
|
||||
/* Destructor */
|
||||
|
||||
CBaseObject::~CBaseObject()
|
||||
{
|
||||
/* Decrement the number of objects active */
|
||||
if (InterlockedDecrement(&m_cObjects) == 0) {
|
||||
if (hlibOLEAut32) {
|
||||
FreeLibrary(hlibOLEAut32);
|
||||
|
||||
hlibOLEAut32 = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
DbgRegisterObjectDestruction(m_dwCookie);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const TCHAR szOle32Aut[] = TEXT("OleAut32.dll");
|
||||
|
||||
HINSTANCE LoadOLEAut32()
|
||||
{
|
||||
if (hlibOLEAut32 == 0) {
|
||||
|
||||
hlibOLEAut32 = LoadLibrary(szOle32Aut);
|
||||
}
|
||||
|
||||
return hlibOLEAut32;
|
||||
}
|
||||
|
||||
|
||||
/* Constructor */
|
||||
|
||||
// We know we use "this" in the initialization list, we also know we don't modify *phr.
|
||||
#pragma warning( disable : 4355 4100 )
|
||||
CUnknown::CUnknown(const TCHAR *pName, LPUNKNOWN pUnk)
|
||||
: CBaseObject(pName)
|
||||
/* Start the object with a reference count of zero - when the */
|
||||
/* object is queried for it's first interface this may be */
|
||||
/* incremented depending on whether or not this object is */
|
||||
/* currently being aggregated upon */
|
||||
, m_cRef(0)
|
||||
/* Set our pointer to our IUnknown interface. */
|
||||
/* If we have an outer, use its, otherwise use ours. */
|
||||
/* This pointer effectivly points to the owner of */
|
||||
/* this object and can be accessed by the GetOwner() method. */
|
||||
, m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<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) ) )
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
CUnknown::CUnknown(const CHAR *pName, LPUNKNOWN pUnk)
|
||||
: CBaseObject(pName), m_cRef(0),
|
||||
m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )
|
||||
{ }
|
||||
|
||||
CUnknown::CUnknown(CHAR *pName, LPUNKNOWN pUnk,HRESULT *phr) :
|
||||
CBaseObject(pName), m_cRef(0),
|
||||
m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )
|
||||
{ }
|
||||
|
||||
#endif
|
||||
|
||||
#pragma warning( default : 4355 4100 )
|
||||
|
||||
|
||||
/* QueryInterface */
|
||||
|
||||
STDMETHODIMP CUnknown::NonDelegatingQueryInterface(REFIID riid, void ** ppv)
|
||||
{
|
||||
CheckPointer(ppv,E_POINTER);
|
||||
ValidateReadWritePtr(ppv,sizeof(PVOID));
|
||||
|
||||
/* We know only about IUnknown */
|
||||
|
||||
if (riid == IID_IUnknown) {
|
||||
GetInterface((LPUNKNOWN) (PNDUNKNOWN) this, ppv);
|
||||
return NOERROR;
|
||||
} else {
|
||||
*ppv = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
/* We have to ensure that we DON'T use a max macro, since these will typically */
|
||||
/* lead to one of the parameters being evaluated twice. Since we are worried */
|
||||
/* about concurrency, we can't afford to access the m_cRef twice since we can't */
|
||||
/* afford to run the risk that its value having changed between accesses. */
|
||||
|
||||
template<class T> inline static T ourmax( const T & a, const T & b )
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
/* AddRef */
|
||||
|
||||
STDMETHODIMP_(ULONG) CUnknown::NonDelegatingAddRef()
|
||||
{
|
||||
LONG lRef = InterlockedIncrement( &m_cRef );
|
||||
ASSERT(lRef > 0);
|
||||
DbgLog((LOG_MEMORY,3,TEXT(" Obj %d ref++ = %d"),
|
||||
m_dwCookie, m_cRef));
|
||||
return ourmax(ULONG(m_cRef), 1ul);
|
||||
}
|
||||
|
||||
|
||||
/* Release */
|
||||
|
||||
STDMETHODIMP_(ULONG) CUnknown::NonDelegatingRelease()
|
||||
{
|
||||
/* If the reference count drops to zero delete ourselves */
|
||||
|
||||
LONG lRef = InterlockedDecrement( &m_cRef );
|
||||
ASSERT(lRef >= 0);
|
||||
|
||||
DbgLog((LOG_MEMORY,3,TEXT(" Object %d ref-- = %d"),
|
||||
m_dwCookie, m_cRef));
|
||||
if (lRef == 0) {
|
||||
|
||||
// COM rules say we must protect against re-entrancy.
|
||||
// If we are an aggregator and we hold our own interfaces
|
||||
// on the aggregatee, the QI for these interfaces will
|
||||
// addref ourselves. So after doing the QI we must release
|
||||
// a ref count on ourselves. Then, before releasing the
|
||||
// private interface, we must addref ourselves. When we do
|
||||
// this from the destructor here it will result in the ref
|
||||
// count going to 1 and then back to 0 causing us to
|
||||
// re-enter the destructor. Hence we add an extra refcount here
|
||||
// once we know we will delete the object.
|
||||
// for an example aggregator see filgraph\distrib.cpp.
|
||||
|
||||
m_cRef++;
|
||||
|
||||
delete this;
|
||||
return ULONG(0);
|
||||
} else {
|
||||
return ourmax(ULONG(m_cRef), 1ul);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return an interface pointer to a requesting client
|
||||
performing a thread safe AddRef as necessary */
|
||||
|
||||
STDAPI GetInterface(LPUNKNOWN pUnk, void **ppv)
|
||||
{
|
||||
CheckPointer(ppv, E_POINTER);
|
||||
*ppv = pUnk;
|
||||
pUnk->AddRef();
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
/* Compares two interfaces and returns TRUE if they are on the same object */
|
||||
|
||||
BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond)
|
||||
{
|
||||
/* Different objects can't have the same interface pointer for
|
||||
any interface
|
||||
*/
|
||||
if (pFirst == pSecond) {
|
||||
return TRUE;
|
||||
}
|
||||
/* OK - do it the hard way - check if they have the same
|
||||
IUnknown pointers - a single object can only have one of these
|
||||
*/
|
||||
LPUNKNOWN pUnknown1; // Retrieve the IUnknown interface
|
||||
LPUNKNOWN pUnknown2; // Retrieve the other IUnknown interface
|
||||
HRESULT hr; // General OLE return code
|
||||
|
||||
ASSERT(pFirst);
|
||||
ASSERT(pSecond);
|
||||
|
||||
/* See if the IUnknown pointers match */
|
||||
|
||||
hr = pFirst->QueryInterface(IID_IUnknown,(void **) &pUnknown1);
|
||||
ASSERT(SUCCEEDED(hr));
|
||||
ASSERT(pUnknown1);
|
||||
|
||||
hr = pSecond->QueryInterface(IID_IUnknown,(void **) &pUnknown2);
|
||||
ASSERT(SUCCEEDED(hr));
|
||||
ASSERT(pUnknown2);
|
||||
|
||||
/* Release the extra interfaces we hold */
|
||||
|
||||
pUnknown1->Release();
|
||||
pUnknown2->Release();
|
||||
return (pUnknown1 == pUnknown2);
|
||||
}
|
||||
|
@ -1,319 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: ComBase.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a class hierarchy for creating
|
||||
// COM objects.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*
|
||||
|
||||
a. Derive your COM object from CUnknown
|
||||
|
||||
b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT *
|
||||
and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls
|
||||
to. The HRESULT * allows error codes to be passed around constructors and
|
||||
the TCHAR * is a descriptive name that can be printed on the debugger.
|
||||
|
||||
It is important that constructors only change the HRESULT * if they have
|
||||
to set an ERROR code, if it was successful then leave it alone or you may
|
||||
overwrite an error code from an object previously created.
|
||||
|
||||
When you call a constructor the descriptive name should be in static store
|
||||
as we do not copy the string. To stop large amounts of memory being used
|
||||
in retail builds by all these static strings use the NAME macro,
|
||||
|
||||
CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class
|
||||
knows not to do anything with objects that don't have a name.
|
||||
|
||||
c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and
|
||||
TCHAR * to the CUnknown constructor. You can set the HRESULT if you have an
|
||||
error, or just simply pass it through to the constructor.
|
||||
|
||||
The object creation will fail in the class factory if the HRESULT indicates
|
||||
an error (ie FAILED(HRESULT) == TRUE)
|
||||
|
||||
d. Create a FactoryTemplate with your object's class id and CreateInstance
|
||||
function.
|
||||
|
||||
Then (for each interface) either
|
||||
|
||||
Multiple inheritance
|
||||
|
||||
1. Also derive it from ISomeInterface
|
||||
2. Include DECLARE_IUNKNOWN in your class definition to declare
|
||||
implementations of QueryInterface, AddRef and Release that
|
||||
call the outer unknown
|
||||
3. Override NonDelegatingQueryInterface to expose ISomeInterface by
|
||||
code something like
|
||||
|
||||
if (riid == IID_ISomeInterface) {
|
||||
return GetInterface((ISomeInterface *) this, ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
4. Declare and implement the member functions of ISomeInterface.
|
||||
|
||||
or: Nested interfaces
|
||||
|
||||
1. Declare a class derived from CUnknown
|
||||
2. Include DECLARE_IUNKNOWN in your class definition
|
||||
3. Override NonDelegatingQueryInterface to expose ISomeInterface by
|
||||
code something like
|
||||
|
||||
if (riid == IID_ISomeInterface) {
|
||||
return GetInterface((ISomeInterface *) this, ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
4. Implement the member functions of ISomeInterface. Use GetOwner() to
|
||||
access the COM object class.
|
||||
|
||||
And in your COM object class:
|
||||
|
||||
5. Make the nested class a friend of the COM object class, and declare
|
||||
an instance of the nested class as a member of the COM object class.
|
||||
|
||||
NOTE that because you must always pass the outer unknown and an hResult
|
||||
to the CUnknown constructor you cannot use a default constructor, in
|
||||
other words you will have to make the member variable a pointer to the
|
||||
class and make a NEW call in your constructor to actually create it.
|
||||
|
||||
6. override the NonDelegatingQueryInterface with code like this:
|
||||
|
||||
if (riid == IID_ISomeInterface) {
|
||||
return m_pImplFilter->
|
||||
NonDelegatingQueryInterface(IID_ISomeInterface, ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
You can have mixed classes which support some interfaces via multiple
|
||||
inheritance and some via nested classes
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __COMBASE__
|
||||
#define __COMBASE__
|
||||
|
||||
// Filter Setup data structures no defined in axextend.idl
|
||||
|
||||
typedef REGPINTYPES
|
||||
AMOVIESETUP_MEDIATYPE, * PAMOVIESETUP_MEDIATYPE, * FAR LPAMOVIESETUP_MEDIATYPE;
|
||||
|
||||
typedef REGFILTERPINS
|
||||
AMOVIESETUP_PIN, * PAMOVIESETUP_PIN, * FAR LPAMOVIESETUP_PIN;
|
||||
|
||||
typedef struct _AMOVIESETUP_FILTER
|
||||
{
|
||||
const CLSID * clsID;
|
||||
const WCHAR * strName;
|
||||
DWORD dwMerit;
|
||||
UINT nPins;
|
||||
const AMOVIESETUP_PIN * lpPin;
|
||||
}
|
||||
AMOVIESETUP_FILTER, * PAMOVIESETUP_FILTER, * FAR LPAMOVIESETUP_FILTER;
|
||||
|
||||
/* The DLLENTRY module initialises the module handle on loading */
|
||||
|
||||
extern HINSTANCE g_hInst;
|
||||
|
||||
/* On DLL load remember which platform we are running on */
|
||||
|
||||
extern DWORD g_amPlatform;
|
||||
extern OSVERSIONINFO g_osInfo; // Filled in by GetVersionEx
|
||||
|
||||
/* Version of IUnknown that is renamed to allow a class to support both
|
||||
non delegating and delegating IUnknowns in the same COM object */
|
||||
|
||||
#ifndef INONDELEGATINGUNKNOWN_DEFINED
|
||||
DECLARE_INTERFACE(INonDelegatingUnknown)
|
||||
{
|
||||
STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE;
|
||||
STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE;
|
||||
STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE;
|
||||
};
|
||||
#define INONDELEGATINGUNKNOWN_DEFINED
|
||||
#endif
|
||||
|
||||
typedef INonDelegatingUnknown *PNDUNKNOWN;
|
||||
|
||||
|
||||
/* This is the base object class that supports active object counting. As
|
||||
part of the debug facilities we trace every time a C++ object is created
|
||||
or destroyed. The name of the object has to be passed up through the class
|
||||
derivation list during construction as you cannot call virtual functions
|
||||
in the constructor. The downside of all this is that every single object
|
||||
constructor has to take an object name parameter that describes it */
|
||||
|
||||
class CBaseObject
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
// Disable the copy constructor and assignment by default so you will get
|
||||
// compiler errors instead of unexpected behaviour if you pass objects
|
||||
// by value or assign objects.
|
||||
CBaseObject(const CBaseObject& objectSrc); // no implementation
|
||||
void operator=(const CBaseObject& objectSrc); // no implementation
|
||||
|
||||
private:
|
||||
static LONG m_cObjects; /* Total number of objects active */
|
||||
|
||||
protected:
|
||||
#ifdef DEBUG
|
||||
DWORD m_dwCookie; /* Cookie identifying this object */
|
||||
#endif
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/* These increment and decrement the number of active objects */
|
||||
|
||||
CBaseObject(const TCHAR *pName);
|
||||
#ifdef UNICODE
|
||||
CBaseObject(const char *pName);
|
||||
#endif
|
||||
~CBaseObject();
|
||||
|
||||
/* Call this to find if there are any CUnknown derived objects active */
|
||||
|
||||
static LONG ObjectsActive() {
|
||||
return m_cObjects;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* An object that supports one or more COM interfaces will be based on
|
||||
this class. It supports counting of total objects for DLLCanUnloadNow
|
||||
support, and an implementation of the core non delegating IUnknown */
|
||||
|
||||
class AM_NOVTABLE CUnknown : public INonDelegatingUnknown,
|
||||
public CBaseObject
|
||||
{
|
||||
private:
|
||||
const LPUNKNOWN m_pUnknown; /* Owner of this object */
|
||||
|
||||
protected: /* So we can override NonDelegatingRelease() */
|
||||
volatile LONG m_cRef; /* Number of reference counts */
|
||||
|
||||
public:
|
||||
|
||||
CUnknown(const TCHAR *pName, LPUNKNOWN pUnk);
|
||||
virtual ~CUnknown() {};
|
||||
|
||||
// This is redundant, just use the other constructor
|
||||
// as we never touch the HRESULT in this anyway
|
||||
CUnknown(TCHAR *pName, LPUNKNOWN pUnk,HRESULT *phr);
|
||||
#ifdef UNICODE
|
||||
CUnknown(const char *pName, LPUNKNOWN pUnk);
|
||||
CUnknown(char *pName, LPUNKNOWN pUnk,HRESULT *phr);
|
||||
#endif
|
||||
|
||||
/* Return the owner of this object */
|
||||
|
||||
LPUNKNOWN GetOwner() const {
|
||||
return m_pUnknown;
|
||||
};
|
||||
|
||||
/* Called from the class factory to create a new instance, it is
|
||||
pure virtual so it must be overriden in your derived class */
|
||||
|
||||
/* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */
|
||||
|
||||
/* Non delegating unknown implementation */
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID, void **);
|
||||
STDMETHODIMP_(ULONG) NonDelegatingAddRef();
|
||||
STDMETHODIMP_(ULONG) NonDelegatingRelease();
|
||||
};
|
||||
|
||||
#if (_MSC_VER <= 1200)
|
||||
#pragma warning(disable:4211)
|
||||
|
||||
/* The standard InterlockedXXX functions won't take volatiles */
|
||||
static inline LONG WINAPI InterlockedIncrement( volatile LONG * plong )
|
||||
{ return InterlockedIncrement( const_cast<LONG*>( plong ) ); }
|
||||
|
||||
static inline LONG WINAPI InterlockedDecrement( volatile LONG * plong )
|
||||
{ return InterlockedDecrement( const_cast<LONG*>( plong ) ); }
|
||||
|
||||
#pragma warning(default:4211)
|
||||
#endif
|
||||
|
||||
|
||||
/* Return an interface pointer to a requesting client
|
||||
performing a thread safe AddRef as necessary */
|
||||
|
||||
STDAPI GetInterface(LPUNKNOWN pUnk, void **ppv);
|
||||
|
||||
/* A function that can create a new COM object */
|
||||
|
||||
typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(LPUNKNOWN pUnkOuter, HRESULT *phr);
|
||||
|
||||
/* A function (can be NULL) which is called from the DLL entrypoint
|
||||
routine for each factory template:
|
||||
|
||||
bLoading - TRUE on DLL load, FALSE on DLL unload
|
||||
rclsid - the m_ClsID of the entry
|
||||
*/
|
||||
typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);
|
||||
|
||||
/* Create one of these per object class in an array so that
|
||||
the default class factory code can create new instances */
|
||||
|
||||
class CFactoryTemplate {
|
||||
|
||||
public:
|
||||
|
||||
const WCHAR * m_Name;
|
||||
const CLSID * m_ClsID;
|
||||
LPFNNewCOMObject m_lpfnNew;
|
||||
LPFNInitRoutine m_lpfnInit;
|
||||
const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter;
|
||||
|
||||
BOOL IsClassID(REFCLSID rclsid) const {
|
||||
return (IsEqualCLSID(*m_ClsID,rclsid));
|
||||
};
|
||||
|
||||
CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr) const {
|
||||
CheckPointer(phr,NULL);
|
||||
return m_lpfnNew(pUnk, phr);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* You must override the (pure virtual) NonDelegatingQueryInterface to return
|
||||
interface pointers (using GetInterface) to the interfaces your derived
|
||||
class supports (the default implementation only supports IUnknown) */
|
||||
|
||||
#define DECLARE_IUNKNOWN \
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { \
|
||||
return GetOwner()->QueryInterface(riid,ppv); \
|
||||
}; \
|
||||
STDMETHODIMP_(ULONG) AddRef() { \
|
||||
return GetOwner()->AddRef(); \
|
||||
}; \
|
||||
STDMETHODIMP_(ULONG) Release() { \
|
||||
return GetOwner()->Release(); \
|
||||
};
|
||||
|
||||
|
||||
|
||||
HINSTANCE LoadOLEAut32();
|
||||
|
||||
|
||||
#endif /* __COMBASE__ */
|
||||
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,919 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: CtlUtil.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Base classes implementing IDispatch parsing for the basic control dual
|
||||
// interfaces. Derive from these and implement just the custom method and
|
||||
// property methods. We also implement CPosPassThru that can be used by
|
||||
// renderers and transforms to pass by IMediaPosition and IMediaSeeking
|
||||
|
||||
#ifndef __CTLUTIL__
|
||||
#define __CTLUTIL__
|
||||
|
||||
// OLE Automation has different ideas of TRUE and FALSE
|
||||
|
||||
#define OATRUE (-1)
|
||||
#define OAFALSE (0)
|
||||
|
||||
|
||||
// It's possible that we could replace this class with CreateStdDispatch
|
||||
|
||||
class CBaseDispatch
|
||||
{
|
||||
ITypeInfo * m_pti;
|
||||
|
||||
public:
|
||||
|
||||
CBaseDispatch() : m_pti(NULL) {}
|
||||
~CBaseDispatch();
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
REFIID riid,
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
OLECHAR ** rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
DISPID * rgdispid);
|
||||
};
|
||||
|
||||
|
||||
class AM_NOVTABLE CMediaControl :
|
||||
public IMediaControl,
|
||||
public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CMediaControl(const TCHAR *, LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
OLECHAR ** rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
DISPPARAMS * pdispparams,
|
||||
VARIANT * pvarResult,
|
||||
EXCEPINFO * pexcepinfo,
|
||||
UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
class AM_NOVTABLE CMediaEvent :
|
||||
public IMediaEventEx,
|
||||
public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CMediaEvent(const TCHAR *, LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
OLECHAR ** rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
DISPPARAMS * pdispparams,
|
||||
VARIANT * pvarResult,
|
||||
EXCEPINFO * pexcepinfo,
|
||||
UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
class AM_NOVTABLE CMediaPosition :
|
||||
public IMediaPosition,
|
||||
public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
CMediaPosition(const TCHAR *, LPUNKNOWN);
|
||||
CMediaPosition(const TCHAR *, LPUNKNOWN, HRESULT *phr);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
OLECHAR ** rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
DISPPARAMS * pdispparams,
|
||||
VARIANT * pvarResult,
|
||||
EXCEPINFO * pexcepinfo,
|
||||
UINT * puArgErr);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// OA-compatibility means that we must use double as the RefTime value,
|
||||
// and REFERENCE_TIME (essentially a LONGLONG) within filters.
|
||||
// this class converts between the two
|
||||
|
||||
class COARefTime : public CRefTime {
|
||||
public:
|
||||
|
||||
COARefTime() {
|
||||
};
|
||||
|
||||
COARefTime(CRefTime t)
|
||||
: CRefTime(t)
|
||||
{
|
||||
};
|
||||
|
||||
COARefTime(REFERENCE_TIME t)
|
||||
: CRefTime(t)
|
||||
{
|
||||
};
|
||||
|
||||
COARefTime(double d) {
|
||||
m_time = (LONGLONG) (d * 10000000);
|
||||
};
|
||||
|
||||
operator double() {
|
||||
return double(m_time) / 10000000;
|
||||
};
|
||||
|
||||
operator REFERENCE_TIME() {
|
||||
return m_time;
|
||||
};
|
||||
|
||||
COARefTime& operator=(const double& rd) {
|
||||
m_time = (LONGLONG) (rd * 10000000);
|
||||
return *this;
|
||||
}
|
||||
|
||||
COARefTime& operator=(const REFERENCE_TIME& rt) {
|
||||
m_time = rt;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline BOOL operator==(const COARefTime& rt)
|
||||
{
|
||||
return m_time == rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator!=(const COARefTime& rt)
|
||||
{
|
||||
return m_time != rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator < (const COARefTime& rt)
|
||||
{
|
||||
return m_time < rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator > (const COARefTime& rt)
|
||||
{
|
||||
return m_time > rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator >= (const COARefTime& rt)
|
||||
{
|
||||
return m_time >= rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator <= (const COARefTime& rt)
|
||||
{
|
||||
return m_time <= rt.m_time;
|
||||
};
|
||||
|
||||
inline COARefTime operator+(const COARefTime& rt)
|
||||
{
|
||||
return COARefTime(m_time + rt.m_time);
|
||||
};
|
||||
|
||||
inline COARefTime operator-(const COARefTime& rt)
|
||||
{
|
||||
return COARefTime(m_time - rt.m_time);
|
||||
};
|
||||
|
||||
inline COARefTime operator*(LONG l)
|
||||
{
|
||||
return COARefTime(m_time * l);
|
||||
};
|
||||
|
||||
inline COARefTime operator/(LONG l)
|
||||
{
|
||||
return COARefTime(m_time / l);
|
||||
};
|
||||
|
||||
private:
|
||||
// Prevent bugs from constructing from LONG (which gets
|
||||
// converted to double and then multiplied by 10000000
|
||||
COARefTime(LONG);
|
||||
int operator=(LONG);
|
||||
};
|
||||
|
||||
|
||||
// A utility class that handles IMediaPosition and IMediaSeeking on behalf
|
||||
// of single-input pin renderers, or transform filters.
|
||||
//
|
||||
// Renderers will expose this from the filter; transform filters will
|
||||
// expose it from the output pin and not the renderer.
|
||||
//
|
||||
// Create one of these, giving it your IPin* for your input pin, and delegate
|
||||
// all IMediaPosition methods to it. It will query the input pin for
|
||||
// IMediaPosition and respond appropriately.
|
||||
//
|
||||
// Call ForceRefresh if the pin connection changes.
|
||||
//
|
||||
// This class no longer caches the upstream IMediaPosition or IMediaSeeking
|
||||
// it acquires it on each method call. This means ForceRefresh is not needed.
|
||||
// The method is kept for source compatibility and to minimise the changes
|
||||
// if we need to put it back later for performance reasons.
|
||||
|
||||
class CPosPassThru : public IMediaSeeking, public CMediaPosition
|
||||
{
|
||||
IPin *m_pPin;
|
||||
|
||||
HRESULT GetPeer(IMediaPosition **ppMP);
|
||||
HRESULT GetPeerSeeking(IMediaSeeking **ppMS);
|
||||
|
||||
public:
|
||||
|
||||
CPosPassThru(const TCHAR *, LPUNKNOWN, HRESULT*, IPin *);
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
HRESULT ForceRefresh() {
|
||||
return S_OK;
|
||||
};
|
||||
|
||||
// override to return an accurate current position
|
||||
virtual HRESULT GetMediaTime(LONGLONG *pStartTime,LONGLONG *pEndTime) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv);
|
||||
|
||||
// IMediaSeeking methods
|
||||
STDMETHODIMP GetCapabilities( DWORD * pCapabilities );
|
||||
STDMETHODIMP CheckCapabilities( DWORD * pCapabilities );
|
||||
STDMETHODIMP SetTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP GetTimeFormat(GUID *pFormat);
|
||||
STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP IsFormatSupported( const GUID * pFormat);
|
||||
STDMETHODIMP QueryPreferredFormat( GUID *pFormat);
|
||||
STDMETHODIMP ConvertTimeFormat(LONGLONG * pTarget, const GUID * pTargetFormat,
|
||||
LONGLONG Source, const GUID * pSourceFormat );
|
||||
STDMETHODIMP SetPositions( LONGLONG * pCurrent, DWORD CurrentFlags
|
||||
, LONGLONG * pStop, DWORD StopFlags );
|
||||
|
||||
STDMETHODIMP GetPositions( LONGLONG * pCurrent, LONGLONG * pStop );
|
||||
STDMETHODIMP GetCurrentPosition( LONGLONG * pCurrent );
|
||||
STDMETHODIMP GetStopPosition( LONGLONG * pStop );
|
||||
STDMETHODIMP SetRate( double dRate);
|
||||
STDMETHODIMP GetRate( double * pdRate);
|
||||
STDMETHODIMP GetDuration( LONGLONG *pDuration);
|
||||
STDMETHODIMP GetAvailable( LONGLONG *pEarliest, LONGLONG *pLatest );
|
||||
STDMETHODIMP GetPreroll( LONGLONG *pllPreroll );
|
||||
|
||||
// IMediaPosition properties
|
||||
STDMETHODIMP get_Duration(REFTIME * plength);
|
||||
STDMETHODIMP put_CurrentPosition(REFTIME llTime);
|
||||
STDMETHODIMP get_StopTime(REFTIME * pllTime);
|
||||
STDMETHODIMP put_StopTime(REFTIME llTime);
|
||||
STDMETHODIMP get_PrerollTime(REFTIME * pllTime);
|
||||
STDMETHODIMP put_PrerollTime(REFTIME llTime);
|
||||
STDMETHODIMP get_Rate(double * pdRate);
|
||||
STDMETHODIMP put_Rate(double dRate);
|
||||
STDMETHODIMP get_CurrentPosition(REFTIME * pllTime);
|
||||
STDMETHODIMP CanSeekForward(LONG *pCanSeekForward);
|
||||
STDMETHODIMP CanSeekBackward(LONG *pCanSeekBackward);
|
||||
|
||||
private:
|
||||
HRESULT GetSeekingLongLong( HRESULT (__stdcall IMediaSeeking::*pMethod)( LONGLONG * ),
|
||||
LONGLONG * pll );
|
||||
};
|
||||
|
||||
|
||||
// Adds the ability to return a current position
|
||||
|
||||
class CRendererPosPassThru : public CPosPassThru
|
||||
{
|
||||
CCritSec m_PositionLock; // Locks access to our position
|
||||
LONGLONG m_StartMedia; // Start media time last seen
|
||||
LONGLONG m_EndMedia; // And likewise the end media
|
||||
BOOL m_bReset; // Have media times been set
|
||||
|
||||
public:
|
||||
|
||||
// Used to help with passing media times through graph
|
||||
|
||||
CRendererPosPassThru(const TCHAR *, LPUNKNOWN, HRESULT*, IPin *);
|
||||
HRESULT RegisterMediaTime(IMediaSample *pMediaSample);
|
||||
HRESULT RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime);
|
||||
HRESULT GetMediaTime(LONGLONG *pStartTime,LONGLONG *pEndTime);
|
||||
HRESULT ResetMediaTime();
|
||||
HRESULT EOS();
|
||||
};
|
||||
|
||||
STDAPI CreatePosPassThru(
|
||||
LPUNKNOWN pAgg,
|
||||
BOOL bRenderer,
|
||||
IPin *pPin,
|
||||
IUnknown **ppPassThru
|
||||
);
|
||||
|
||||
// A class that handles the IDispatch part of IBasicAudio and leaves the
|
||||
// properties and methods themselves pure virtual.
|
||||
|
||||
class AM_NOVTABLE CBasicAudio : public IBasicAudio, public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CBasicAudio(const TCHAR *, LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
OLECHAR ** rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
DISPPARAMS * pdispparams,
|
||||
VARIANT * pvarResult,
|
||||
EXCEPINFO * pexcepinfo,
|
||||
UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
// A class that handles the IDispatch part of IBasicVideo and leaves the
|
||||
// properties and methods themselves pure virtual.
|
||||
|
||||
class AM_NOVTABLE CBaseBasicVideo : public IBasicVideo2, public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CBaseBasicVideo(const TCHAR *, LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
OLECHAR ** rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
DISPPARAMS * pdispparams,
|
||||
VARIANT * pvarResult,
|
||||
EXCEPINFO * pexcepinfo,
|
||||
UINT * puArgErr);
|
||||
|
||||
STDMETHODIMP GetPreferredAspectRatio(
|
||||
long *plAspectX,
|
||||
long *plAspectY)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// A class that handles the IDispatch part of IVideoWindow and leaves the
|
||||
// properties and methods themselves pure virtual.
|
||||
|
||||
class AM_NOVTABLE CBaseVideoWindow : public IVideoWindow, public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CBaseVideoWindow(const TCHAR *, LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
OLECHAR ** rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
DISPPARAMS * pdispparams,
|
||||
VARIANT * pvarResult,
|
||||
EXCEPINFO * pexcepinfo,
|
||||
UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
// abstract class to help source filters with their implementation
|
||||
// of IMediaPosition. Derive from this and set the duration (and stop
|
||||
// position). Also override NotifyChange to do something when the properties
|
||||
// change.
|
||||
|
||||
class AM_NOVTABLE CSourcePosition : public CMediaPosition
|
||||
{
|
||||
|
||||
public:
|
||||
CSourcePosition(const TCHAR *, LPUNKNOWN, HRESULT*, CCritSec *);
|
||||
|
||||
// IMediaPosition methods
|
||||
STDMETHODIMP get_Duration(REFTIME * plength);
|
||||
STDMETHODIMP put_CurrentPosition(REFTIME llTime);
|
||||
STDMETHODIMP get_StopTime(REFTIME * pllTime);
|
||||
STDMETHODIMP put_StopTime(REFTIME llTime);
|
||||
STDMETHODIMP get_PrerollTime(REFTIME * pllTime);
|
||||
STDMETHODIMP put_PrerollTime(REFTIME llTime);
|
||||
STDMETHODIMP get_Rate(double * pdRate);
|
||||
STDMETHODIMP put_Rate(double dRate);
|
||||
STDMETHODIMP CanSeekForward(LONG *pCanSeekForward);
|
||||
STDMETHODIMP CanSeekBackward(LONG *pCanSeekBackward);
|
||||
|
||||
// override if you can return the data you are actually working on
|
||||
STDMETHODIMP get_CurrentPosition(REFTIME * pllTime) {
|
||||
return E_NOTIMPL;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
// we call this to notify changes. Override to handle them
|
||||
virtual HRESULT ChangeStart() PURE;
|
||||
virtual HRESULT ChangeStop() PURE;
|
||||
virtual HRESULT ChangeRate() PURE;
|
||||
|
||||
COARefTime m_Duration;
|
||||
COARefTime m_Start;
|
||||
COARefTime m_Stop;
|
||||
double m_Rate;
|
||||
|
||||
CCritSec * m_pLock;
|
||||
};
|
||||
|
||||
class AM_NOVTABLE CSourceSeeking :
|
||||
public IMediaSeeking,
|
||||
public CUnknown
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
DECLARE_IUNKNOWN;
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
|
||||
|
||||
// IMediaSeeking methods
|
||||
|
||||
STDMETHODIMP IsFormatSupported(const GUID * pFormat);
|
||||
STDMETHODIMP QueryPreferredFormat(GUID *pFormat);
|
||||
STDMETHODIMP SetTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP GetTimeFormat(GUID *pFormat);
|
||||
STDMETHODIMP GetDuration(LONGLONG *pDuration);
|
||||
STDMETHODIMP GetStopPosition(LONGLONG *pStop);
|
||||
STDMETHODIMP GetCurrentPosition(LONGLONG *pCurrent);
|
||||
STDMETHODIMP GetCapabilities( DWORD * pCapabilities );
|
||||
STDMETHODIMP CheckCapabilities( DWORD * pCapabilities );
|
||||
STDMETHODIMP ConvertTimeFormat( LONGLONG * pTarget, const GUID * pTargetFormat,
|
||||
LONGLONG Source, const GUID * pSourceFormat );
|
||||
|
||||
STDMETHODIMP SetPositions( LONGLONG * pCurrent, DWORD CurrentFlags
|
||||
, LONGLONG * pStop, DWORD StopFlags );
|
||||
|
||||
STDMETHODIMP GetPositions( LONGLONG * pCurrent, LONGLONG * pStop );
|
||||
|
||||
STDMETHODIMP GetAvailable( LONGLONG * pEarliest, LONGLONG * pLatest );
|
||||
STDMETHODIMP SetRate( double dRate);
|
||||
STDMETHODIMP GetRate( double * pdRate);
|
||||
STDMETHODIMP GetPreroll(LONGLONG *pPreroll);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// ctor
|
||||
CSourceSeeking(const TCHAR *, LPUNKNOWN, HRESULT*, CCritSec *);
|
||||
|
||||
// we call this to notify changes. Override to handle them
|
||||
virtual HRESULT ChangeStart() PURE;
|
||||
virtual HRESULT ChangeStop() PURE;
|
||||
virtual HRESULT ChangeRate() PURE;
|
||||
|
||||
CRefTime m_rtDuration; // length of stream
|
||||
CRefTime m_rtStart; // source will start here
|
||||
CRefTime m_rtStop; // source will stop here
|
||||
double m_dRateSeeking;
|
||||
|
||||
// seeking capabilities
|
||||
DWORD m_dwSeekingCaps;
|
||||
|
||||
CCritSec * m_pLock;
|
||||
};
|
||||
|
||||
|
||||
// Base classes supporting Deferred commands.
|
||||
|
||||
// Deferred commands are queued by calls to methods on the IQueueCommand
|
||||
// interface, exposed by the filtergraph and by some filters. A successful
|
||||
// call to one of these methods will return an IDeferredCommand interface
|
||||
// representing the queued command.
|
||||
//
|
||||
// A CDeferredCommand object represents a single deferred command, and exposes
|
||||
// the IDeferredCommand interface as well as other methods permitting time
|
||||
// checks and actual execution. It contains a reference to the CCommandQueue
|
||||
// object on which it is queued.
|
||||
//
|
||||
// CCommandQueue is a base class providing a queue of CDeferredCommand
|
||||
// objects, and methods to add, remove, check status and invoke the queued
|
||||
// commands. A CCommandQueue object would be part of an object that
|
||||
// implemented IQueueCommand.
|
||||
|
||||
class CCmdQueue;
|
||||
|
||||
// take a copy of the params and store them. Release any allocated
|
||||
// memory in destructor
|
||||
|
||||
class CDispParams : public DISPPARAMS
|
||||
{
|
||||
public:
|
||||
CDispParams(UINT nArgs, VARIANT* pArgs, HRESULT *phr = NULL);
|
||||
~CDispParams();
|
||||
};
|
||||
|
||||
|
||||
// CDeferredCommand lifetime is controlled by refcounts. Caller of
|
||||
// InvokeAt.. gets a refcounted interface pointer, and the CCmdQueue
|
||||
// object also holds a refcount on us. Calling Cancel or Invoke takes
|
||||
// us off the CCmdQueue and thus reduces the refcount by 1. Once taken
|
||||
// off the queue we cannot be put back on the queue.
|
||||
|
||||
class CDeferredCommand
|
||||
: public CUnknown,
|
||||
public IDeferredCommand
|
||||
{
|
||||
public:
|
||||
|
||||
CDeferredCommand(
|
||||
CCmdQueue * pQ,
|
||||
LPUNKNOWN pUnk, // aggregation outer unk
|
||||
HRESULT * phr,
|
||||
LPUNKNOWN pUnkExecutor, // object that will execute this cmd
|
||||
REFTIME time,
|
||||
GUID* iid,
|
||||
long dispidMethod,
|
||||
short wFlags,
|
||||
long cArgs,
|
||||
VARIANT* pDispParams,
|
||||
VARIANT* pvarResult,
|
||||
short* puArgErr,
|
||||
BOOL bStream
|
||||
);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
|
||||
|
||||
// IDeferredCommand methods
|
||||
STDMETHODIMP Cancel();
|
||||
STDMETHODIMP Confidence(
|
||||
LONG* pConfidence);
|
||||
STDMETHODIMP Postpone(
|
||||
REFTIME newtime);
|
||||
STDMETHODIMP GetHResult(
|
||||
HRESULT* phrResult);
|
||||
|
||||
// other public methods
|
||||
|
||||
HRESULT Invoke();
|
||||
|
||||
// access methods
|
||||
|
||||
// returns TRUE if streamtime, FALSE if presentation time
|
||||
BOOL IsStreamTime() {
|
||||
return m_bStream;
|
||||
};
|
||||
|
||||
CRefTime GetTime() {
|
||||
return m_time;
|
||||
};
|
||||
|
||||
REFIID GetIID() {
|
||||
return *m_iid;
|
||||
};
|
||||
|
||||
long GetMethod() {
|
||||
return m_dispidMethod;
|
||||
};
|
||||
|
||||
short GetFlags() {
|
||||
return m_wFlags;
|
||||
};
|
||||
|
||||
DISPPARAMS* GetParams() {
|
||||
return &m_DispParams;
|
||||
};
|
||||
|
||||
VARIANT* GetResult() {
|
||||
return m_pvarResult;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
CCmdQueue* m_pQueue;
|
||||
|
||||
// pUnk for the interface that we will execute the command on
|
||||
LPUNKNOWN m_pUnk;
|
||||
|
||||
// stored command data
|
||||
REFERENCE_TIME m_time;
|
||||
GUID* m_iid;
|
||||
long m_dispidMethod;
|
||||
short m_wFlags;
|
||||
VARIANT* m_pvarResult;
|
||||
BOOL m_bStream;
|
||||
CDispParams m_DispParams;
|
||||
DISPID m_DispId; // For get and put
|
||||
|
||||
// we use this for ITypeInfo access
|
||||
CBaseDispatch m_Dispatch;
|
||||
|
||||
// save retval here
|
||||
HRESULT m_hrResult;
|
||||
};
|
||||
|
||||
|
||||
// a list of CDeferredCommand objects. this is a base class providing
|
||||
// the basics of access to the list. If you want to use CDeferredCommand
|
||||
// objects then your queue needs to be derived from this class.
|
||||
|
||||
class AM_NOVTABLE CCmdQueue
|
||||
{
|
||||
public:
|
||||
CCmdQueue();
|
||||
virtual ~CCmdQueue();
|
||||
|
||||
// returns a new CDeferredCommand object that will be initialised with
|
||||
// the parameters and will be added to the queue during construction.
|
||||
// returns S_OK if successfully created otherwise an error and
|
||||
// no object has been queued.
|
||||
virtual HRESULT New(
|
||||
CDeferredCommand **ppCmd,
|
||||
LPUNKNOWN pUnk,
|
||||
REFTIME time,
|
||||
GUID* iid,
|
||||
long dispidMethod,
|
||||
short wFlags,
|
||||
long cArgs,
|
||||
VARIANT* pDispParams,
|
||||
VARIANT* pvarResult,
|
||||
short* puArgErr,
|
||||
BOOL bStream
|
||||
);
|
||||
|
||||
// called by the CDeferredCommand object to add and remove itself
|
||||
// from the queue
|
||||
virtual HRESULT Insert(CDeferredCommand* pCmd);
|
||||
virtual HRESULT Remove(CDeferredCommand* pCmd);
|
||||
|
||||
// Command-Due Checking
|
||||
//
|
||||
// There are two schemes of synchronisation: coarse and accurate. In
|
||||
// coarse mode, you wait till the time arrives and then execute the cmd.
|
||||
// In accurate mode, you wait until you are processing the sample that
|
||||
// will appear at the time, and then execute the command. It's up to the
|
||||
// filter which one it will implement. The filtergraph will always
|
||||
// implement coarse mode for commands queued at the filtergraph.
|
||||
//
|
||||
// If you want coarse sync, you probably want to wait until there is a
|
||||
// command due, and then execute it. You can do this by calling
|
||||
// GetDueCommand. If you have several things to wait for, get the
|
||||
// event handle from GetDueHandle() and when this is signalled then call
|
||||
// GetDueCommand. Stream time will only advance between calls to Run and
|
||||
// EndRun. Note that to avoid an extra thread there is no guarantee that
|
||||
// if the handle is set there will be a command ready. Each time the
|
||||
// event is signalled, call GetDueCommand (probably with a 0 timeout);
|
||||
// This may return E_ABORT.
|
||||
//
|
||||
// If you want accurate sync, you must call GetCommandDueFor, passing
|
||||
// as a parameter the stream time of the samples you are about to process.
|
||||
// This will return:
|
||||
// -- a stream-time command due at or before that stream time
|
||||
// -- a presentation-time command due at or before the
|
||||
// time that stream time will be presented (only between Run
|
||||
// and EndRun calls, since outside of this, the mapping from
|
||||
// stream time to presentation time is not known.
|
||||
// -- any presentation-time command due now.
|
||||
// This means that if you want accurate synchronisation on samples that
|
||||
// might be processed during Paused mode, you need to use
|
||||
// stream-time commands.
|
||||
//
|
||||
// In all cases, commands remain queued until Invoked or Cancelled. The
|
||||
// setting and resetting of the event handle is managed entirely by this
|
||||
// queue object.
|
||||
|
||||
// set the clock used for timing
|
||||
virtual HRESULT SetSyncSource(IReferenceClock*);
|
||||
|
||||
// switch to run mode. Streamtime to Presentation time mapping known.
|
||||
virtual HRESULT Run(REFERENCE_TIME tStreamTimeOffset);
|
||||
|
||||
// switch to Stopped or Paused mode. Time mapping not known.
|
||||
virtual HRESULT EndRun();
|
||||
|
||||
// return a pointer to the next due command. Blocks for msTimeout
|
||||
// milliseconds until there is a due command.
|
||||
// Stream-time commands will only become due between Run and Endrun calls.
|
||||
// The command remains queued until invoked or cancelled.
|
||||
// Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).
|
||||
// Returns an AddRef-ed object
|
||||
virtual HRESULT GetDueCommand(CDeferredCommand ** ppCmd, long msTimeout);
|
||||
|
||||
// return the event handle that will be signalled whenever
|
||||
// there are deferred commands due for execution (when GetDueCommand
|
||||
// will not block).
|
||||
HANDLE GetDueHandle() {
|
||||
return HANDLE(m_evDue);
|
||||
};
|
||||
|
||||
// return a pointer to a command that will be due for a given time.
|
||||
// Pass in a stream time here. The stream time offset will be passed
|
||||
// in via the Run method.
|
||||
// Commands remain queued until invoked or cancelled.
|
||||
// This method will not block. It will report VFW_E_NOT_FOUND if there
|
||||
// are no commands due yet.
|
||||
// Returns an AddRef-ed object
|
||||
virtual HRESULT GetCommandDueFor(REFERENCE_TIME tStream, CDeferredCommand**ppCmd);
|
||||
|
||||
// check if a given time is due (TRUE if it is due yet)
|
||||
BOOL CheckTime(CRefTime time, BOOL bStream) {
|
||||
|
||||
// if no clock, nothing is due!
|
||||
if (!m_pClock) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// stream time
|
||||
if (bStream) {
|
||||
|
||||
// not valid if not running
|
||||
if (!m_bRunning) {
|
||||
return FALSE;
|
||||
}
|
||||
// add on known stream time offset to get presentation time
|
||||
time += m_StreamTimeOffset;
|
||||
}
|
||||
|
||||
CRefTime Now;
|
||||
m_pClock->GetTime((REFERENCE_TIME*)&Now);
|
||||
return (time <= Now);
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
// protect access to lists etc
|
||||
CCritSec m_Lock;
|
||||
|
||||
// commands queued in presentation time are stored here
|
||||
CGenericList<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_PTR m_dwAdvise;
|
||||
|
||||
// advise time is for this presentation time
|
||||
CRefTime m_tCurrentAdvise;
|
||||
|
||||
// the reference clock we are using (addrefed)
|
||||
IReferenceClock* m_pClock;
|
||||
|
||||
// true when running
|
||||
BOOL m_bRunning;
|
||||
|
||||
// contains stream time offset when m_bRunning is true
|
||||
CRefTime m_StreamTimeOffset;
|
||||
};
|
||||
|
||||
#endif // __CTLUTIL__
|
@ -1,130 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: DDMM.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements routines for using DirectDraw
|
||||
// on a multimonitor system.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//#include <strsafe.h>
|
||||
#include <strmif.h>
|
||||
#include <mmsystem.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, HMONITOR hMonitor)
|
||||
{
|
||||
FindDeviceData *p = (FindDeviceData*)lParam;
|
||||
|
||||
if (lstrcmpiA(p->szDevice, szDevice) == 0) {
|
||||
if (lpGUID) {
|
||||
p->GUID = *lpGUID;
|
||||
p->lpGUID = &p->GUID;
|
||||
} else {
|
||||
p->lpGUID = NULL;
|
||||
}
|
||||
p->fFound = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DirectDrawCreateFromDevice
|
||||
*
|
||||
* create a DirectDraw object for a particular device
|
||||
*/
|
||||
IDirectDraw * DirectDrawCreateFromDevice(LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, PDRAWENUM DirectDrawEnumerateP)
|
||||
{
|
||||
IDirectDraw* pdd = NULL;
|
||||
FindDeviceData find;
|
||||
|
||||
if (szDevice == NULL) {
|
||||
DirectDrawCreateP(NULL, &pdd, NULL);
|
||||
return pdd;
|
||||
}
|
||||
|
||||
find.szDevice = szDevice;
|
||||
find.fFound = FALSE;
|
||||
DirectDrawEnumerateP(FindDeviceCallback, (LPVOID)&find);
|
||||
|
||||
if (find.fFound)
|
||||
{
|
||||
//
|
||||
// In 4bpp mode the following DDraw call causes a message box to be popped
|
||||
// up by DDraw (!?!). It's DDraw's fault, but we don't like it. So we
|
||||
// make sure it doesn't happen.
|
||||
//
|
||||
UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
DirectDrawCreateP(find.lpGUID, &pdd, NULL);
|
||||
SetErrorMode(ErrorMode);
|
||||
}
|
||||
|
||||
return pdd;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DirectDrawCreateFromDeviceEx
|
||||
*
|
||||
* create a DirectDraw object for a particular device
|
||||
*/
|
||||
IDirectDraw * DirectDrawCreateFromDeviceEx(LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, LPDIRECTDRAWENUMERATEEXA DirectDrawEnumerateExP)
|
||||
{
|
||||
IDirectDraw* pdd = NULL;
|
||||
FindDeviceData find;
|
||||
|
||||
if (szDevice == NULL) {
|
||||
DirectDrawCreateP(NULL, &pdd, NULL);
|
||||
return pdd;
|
||||
}
|
||||
|
||||
find.szDevice = szDevice;
|
||||
find.fFound = FALSE;
|
||||
DirectDrawEnumerateExP(FindDeviceCallbackEx, (LPVOID)&find,
|
||||
DDENUM_ATTACHEDSECONDARYDEVICES);
|
||||
|
||||
if (find.fFound)
|
||||
{
|
||||
//
|
||||
// In 4bpp mode the following DDraw call causes a message box to be popped
|
||||
// up by DDraw (!?!). It's DDraw's fault, but we don't like it. So we
|
||||
// make sure it doesn't happen.
|
||||
//
|
||||
UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
DirectDrawCreateP(find.lpGUID, &pdd, NULL);
|
||||
SetErrorMode(ErrorMode);
|
||||
}
|
||||
|
||||
return pdd;
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: DDMM.h
|
||||
//
|
||||
// Desc: DirectShow base classes - efines routines for using DirectDraw
|
||||
// on a multimonitor system.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { /* Assume C declarations for C++ */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
// DDRAW.H might not include these
|
||||
#ifndef DDENUM_ATTACHEDSECONDARYDEVICES
|
||||
#define DDENUM_ATTACHEDSECONDARYDEVICES 0x00000001L
|
||||
#endif
|
||||
|
||||
typedef HRESULT (*PDRAWCREATE)(IID *,LPDIRECTDRAW *,LPUNKNOWN);
|
||||
typedef HRESULT (*PDRAWENUM)(LPDDENUMCALLBACKA, LPVOID);
|
||||
|
||||
IDirectDraw * DirectDrawCreateFromDevice(LPSTR, PDRAWCREATE, PDRAWENUM);
|
||||
IDirectDraw * DirectDrawCreateFromDeviceEx(LPSTR, PDRAWCREATE, LPDIRECTDRAWENUMERATEEXA);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
@ -1,128 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: DSSchedule.h (replaces DirectX 8's schedule.h)
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __CAMSchedule__
|
||||
#define __CAMSchedule__
|
||||
|
||||
class CAMSchedule : private CBaseObject
|
||||
{
|
||||
public:
|
||||
virtual ~CAMSchedule();
|
||||
// ev is the event we should fire if the advise time needs re-evaluating
|
||||
CAMSchedule( HANDLE ev );
|
||||
|
||||
DWORD GetAdviseCount();
|
||||
REFERENCE_TIME GetNextAdviseTime();
|
||||
|
||||
// We need a method for derived classes to add advise packets, we return the cookie
|
||||
DWORD_PTR AddAdvisePacket( const REFERENCE_TIME & time1, const REFERENCE_TIME & time2, HANDLE h, BOOL periodic );
|
||||
// And a way to cancel
|
||||
HRESULT Unadvise(DWORD_PTR dwAdviseCookie);
|
||||
|
||||
// Tell us the time please, and we'll dispatch the expired events. We return the time of the next event.
|
||||
// NB: The time returned will be "useless" if you start adding extra Advises. But that's the problem of
|
||||
// whoever is using this helper class (typically a clock).
|
||||
REFERENCE_TIME Advise( const REFERENCE_TIME & rtTime );
|
||||
|
||||
// Get the event handle which will be set if advise time requires re-evaluation.
|
||||
HANDLE GetEvent() const { return m_ev; }
|
||||
|
||||
private:
|
||||
// We define the nodes that will be used in our singly linked list
|
||||
// of advise packets. The list is ordered by time, with the
|
||||
// elements that will expire first at the front.
|
||||
class CAdvisePacket
|
||||
{
|
||||
public:
|
||||
CAdvisePacket()
|
||||
{}
|
||||
|
||||
CAdvisePacket * m_next;
|
||||
DWORD_PTR m_dwAdviseCookie;
|
||||
REFERENCE_TIME m_rtEventTime; // Time at which event should be set
|
||||
REFERENCE_TIME m_rtPeriod; // Periodic time
|
||||
HANDLE m_hNotify; // Handle to event or semephore
|
||||
BOOL m_bPeriodic; // TRUE => Periodic event
|
||||
|
||||
CAdvisePacket( CAdvisePacket * next, LONGLONG time ) : m_next(next), m_rtEventTime(time)
|
||||
{}
|
||||
|
||||
void InsertAfter( CAdvisePacket * p )
|
||||
{
|
||||
p->m_next = m_next;
|
||||
m_next = p;
|
||||
}
|
||||
|
||||
int IsZ() const // That is, is it the node that represents the end of the list
|
||||
{ return m_next == 0; }
|
||||
|
||||
CAdvisePacket * RemoveNext()
|
||||
{
|
||||
CAdvisePacket *const next = m_next;
|
||||
CAdvisePacket *const new_next = next->m_next;
|
||||
m_next = new_next;
|
||||
return next;
|
||||
}
|
||||
|
||||
void DeleteNext()
|
||||
{
|
||||
delete RemoveNext();
|
||||
}
|
||||
|
||||
CAdvisePacket * Next() const
|
||||
{
|
||||
CAdvisePacket * result = m_next;
|
||||
if (result->IsZ()) result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD_PTR Cookie() const
|
||||
{ return m_dwAdviseCookie; }
|
||||
};
|
||||
|
||||
// Structure is:
|
||||
// head -> elmt1 -> elmt2 -> z -> null
|
||||
// So an empty list is: head -> z -> null
|
||||
// Having head & z as links makes insertaion,
|
||||
// deletion and shunting much easier.
|
||||
CAdvisePacket head, z; // z is both a tail and a sentry
|
||||
|
||||
volatile DWORD_PTR m_dwNextCookie; // Strictly increasing
|
||||
volatile DWORD m_dwAdviseCount; // Number of elements on list
|
||||
|
||||
CCritSec m_Serialize;
|
||||
|
||||
// AddAdvisePacket: adds the packet, returns the cookie (0 if failed)
|
||||
DWORD_PTR AddAdvisePacket( CAdvisePacket * pPacket );
|
||||
// Event that we should set if the packed added above will be the next to fire.
|
||||
const HANDLE m_ev;
|
||||
|
||||
// A Shunt is where we have changed the first element in the
|
||||
// list and want it re-evaluating (i.e. repositioned) in
|
||||
// the list.
|
||||
void ShuntHead();
|
||||
|
||||
// Rather than delete advise packets, we cache them for future use
|
||||
CAdvisePacket * m_pAdviseCache;
|
||||
DWORD m_dwCacheCount;
|
||||
enum { dwCacheMax = 5 }; // Don't bother caching more than five
|
||||
|
||||
void Delete( CAdvisePacket * pLink );// This "Delete" will cache the Link
|
||||
|
||||
// Attributes and methods for debugging
|
||||
public:
|
||||
#ifdef DEBUG
|
||||
void DumpLinkedList();
|
||||
#else
|
||||
void DumpLinkedList() {}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // __CAMSchedule__
|
@ -1,101 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: FourCC.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// FOURCCMap
|
||||
//
|
||||
// provides a mapping between old-style multimedia format DWORDs
|
||||
// and new-style GUIDs.
|
||||
//
|
||||
// A range of 4 billion GUIDs has been allocated to ensure that this
|
||||
// mapping can be done straightforwardly one-to-one in both directions.
|
||||
//
|
||||
// January 95
|
||||
|
||||
|
||||
#ifndef __FOURCC__
|
||||
#define __FOURCC__
|
||||
|
||||
|
||||
// Multimedia format types are marked with DWORDs built from four 8-bit
|
||||
// chars and known as FOURCCs. New multimedia AM_MEDIA_TYPE definitions include
|
||||
// a subtype GUID. In order to simplify the mapping, GUIDs in the range:
|
||||
// XXXXXXXX-0000-0010-8000-00AA00389B71
|
||||
// are reserved for FOURCCs.
|
||||
|
||||
class FOURCCMap : public GUID
|
||||
{
|
||||
|
||||
public:
|
||||
FOURCCMap();
|
||||
FOURCCMap(DWORD Fourcc);
|
||||
FOURCCMap(const GUID *);
|
||||
|
||||
|
||||
DWORD GetFOURCC(void);
|
||||
void SetFOURCC(DWORD fourcc);
|
||||
void SetFOURCC(const GUID *);
|
||||
|
||||
private:
|
||||
void InitGUID();
|
||||
};
|
||||
|
||||
#define GUID_Data2 0
|
||||
#define GUID_Data3 0x10
|
||||
#define GUID_Data4_1 0xaa000080
|
||||
#define GUID_Data4_2 0x719b3800
|
||||
|
||||
inline void
|
||||
FOURCCMap::InitGUID() {
|
||||
Data2 = GUID_Data2;
|
||||
Data3 = GUID_Data3;
|
||||
((DWORD *)Data4)[0] = GUID_Data4_1;
|
||||
((DWORD *)Data4)[1] = GUID_Data4_2;
|
||||
}
|
||||
|
||||
inline
|
||||
FOURCCMap::FOURCCMap() {
|
||||
InitGUID();
|
||||
SetFOURCC( DWORD(0));
|
||||
}
|
||||
|
||||
inline
|
||||
FOURCCMap::FOURCCMap(DWORD fourcc)
|
||||
{
|
||||
InitGUID();
|
||||
SetFOURCC(fourcc);
|
||||
}
|
||||
|
||||
inline
|
||||
FOURCCMap::FOURCCMap(const GUID * pGuid)
|
||||
{
|
||||
InitGUID();
|
||||
SetFOURCC(pGuid);
|
||||
}
|
||||
|
||||
inline void
|
||||
FOURCCMap::SetFOURCC(const GUID * pGuid)
|
||||
{
|
||||
FOURCCMap * p = (FOURCCMap*) pGuid;
|
||||
SetFOURCC(p->GetFOURCC());
|
||||
}
|
||||
|
||||
inline void
|
||||
FOURCCMap::SetFOURCC(DWORD fourcc)
|
||||
{
|
||||
Data1 = fourcc;
|
||||
}
|
||||
|
||||
inline DWORD
|
||||
FOURCCMap::GetFOURCC(void)
|
||||
{
|
||||
return Data1;
|
||||
}
|
||||
|
||||
#endif /* __FOURCC__ */
|
||||
|
@ -1,222 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Measure.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*
|
||||
The idea is to pepper the source code with interesting measurements and
|
||||
have the last few thousand of these recorded in a circular buffer that
|
||||
can be post-processed to give interesting numbers.
|
||||
|
||||
WHAT THE LOG LOOKS LIKE:
|
||||
|
||||
Time (sec) Type Delta Incident_Name
|
||||
0.055,41 NOTE -. Incident Nine - Another note
|
||||
0.055,42 NOTE 0.000,01 Incident Nine - Another note
|
||||
0.055,44 NOTE 0.000,02 Incident Nine - Another note
|
||||
0.055,45 STOP -. Incident Eight - Also random
|
||||
0.055,47 START -. Incident Seven - Random
|
||||
0.055,49 NOTE 0.000,05 Incident Nine - Another note
|
||||
------- <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__
|
@ -1,120 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: MsgThrd.h
|
||||
//
|
||||
// Desc: DirectShow base classes - provides support for a worker thread
|
||||
// class to which one can asynchronously post messages.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Message class - really just a structure.
|
||||
//
|
||||
class CMsg {
|
||||
public:
|
||||
UINT uMsg;
|
||||
DWORD dwFlags;
|
||||
LPVOID lpParam;
|
||||
CAMEvent *pEvent;
|
||||
|
||||
CMsg(UINT u, DWORD dw, LPVOID lp, CAMEvent *pEvnt)
|
||||
: uMsg(u), dwFlags(dw), lpParam(lp), pEvent(pEvnt) {}
|
||||
|
||||
CMsg()
|
||||
: uMsg(0), dwFlags(0L), lpParam(NULL), pEvent(NULL) {}
|
||||
};
|
||||
|
||||
// This is the actual thread class. It exports all the usual thread control
|
||||
// functions. The created thread is different from a normal WIN32 thread in
|
||||
// that it is prompted to perform particaular tasks by responding to messages
|
||||
// posted to its message queue.
|
||||
//
|
||||
class AM_NOVTABLE CMsgThread {
|
||||
private:
|
||||
static DWORD WINAPI DefaultThreadProc(LPVOID lpParam);
|
||||
DWORD m_ThreadId;
|
||||
HANDLE m_hThread;
|
||||
|
||||
protected:
|
||||
|
||||
// if you want to override GetThreadMsg to block on other things
|
||||
// as well as this queue, you need access to this
|
||||
CGenericList<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;
|
||||
};
|
||||
|
@ -1,477 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: MType.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements a class that holds and
|
||||
// manages media type information.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// helper class that derived pin objects can use to compare media
|
||||
// types etc. Has same data members as the struct AM_MEDIA_TYPE defined
|
||||
// in the streams IDL file, but also has (non-virtual) functions
|
||||
|
||||
#include "streams.h"
|
||||
#include <mmreg.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, HRESULT* phr)
|
||||
{
|
||||
HRESULT hr = CopyMediaType(this, &rt);
|
||||
if (FAILED(hr) && (NULL != phr)) {
|
||||
*phr = hr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CMediaType::CMediaType(const CMediaType& rt, HRESULT* phr)
|
||||
{
|
||||
HRESULT hr = CopyMediaType(this, &rt);
|
||||
if (FAILED(hr) && (NULL != phr)) {
|
||||
*phr = hr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// this class inherits publicly from AM_MEDIA_TYPE so the compiler could generate
|
||||
// the following assignment operator itself, however it could introduce some
|
||||
// memory conflicts and leaks in the process because the structure contains
|
||||
// a dynamically allocated block (pbFormat) which it will not copy correctly
|
||||
|
||||
CMediaType&
|
||||
CMediaType::operator=(const AM_MEDIA_TYPE& rt)
|
||||
{
|
||||
Set(rt);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CMediaType&
|
||||
CMediaType::operator=(const CMediaType& rt)
|
||||
{
|
||||
*this = (AM_MEDIA_TYPE &) rt;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BOOL
|
||||
CMediaType::operator == (const CMediaType& rt) const
|
||||
{
|
||||
// I don't believe we need to check sample size or
|
||||
// temporal compression flags, since I think these must
|
||||
// be represented in the type, subtype and format somehow. They
|
||||
// are pulled out as separate flags so that people who don't understand
|
||||
// the particular format representation can still see them, but
|
||||
// they should duplicate information in the format block.
|
||||
|
||||
return ((IsEqualGUID(majortype,rt.majortype) == TRUE) &&
|
||||
(IsEqualGUID(subtype,rt.subtype) == TRUE) &&
|
||||
(IsEqualGUID(formattype,rt.formattype) == TRUE) &&
|
||||
(cbFormat == rt.cbFormat) &&
|
||||
( (cbFormat == 0) ||
|
||||
(memcmp(pbFormat, rt.pbFormat, cbFormat) == 0)));
|
||||
}
|
||||
|
||||
|
||||
BOOL
|
||||
CMediaType::operator != (const CMediaType& rt) const
|
||||
{
|
||||
/* Check to see if they are equal */
|
||||
|
||||
if (*this == rt) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
CMediaType::Set(const CMediaType& rt)
|
||||
{
|
||||
return Set((AM_MEDIA_TYPE &) rt);
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
CMediaType::Set(const AM_MEDIA_TYPE& rt)
|
||||
{
|
||||
if (&rt != this) {
|
||||
FreeMediaType(*this);
|
||||
HRESULT hr = CopyMediaType(this, &rt);
|
||||
if (FAILED(hr)) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
BOOL
|
||||
CMediaType::IsValid() const
|
||||
{
|
||||
return (!IsEqualGUID(majortype,GUID_NULL));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetType(const GUID* ptype)
|
||||
{
|
||||
majortype = *ptype;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetSubtype(const GUID* ptype)
|
||||
{
|
||||
subtype = *ptype;
|
||||
}
|
||||
|
||||
|
||||
ULONG
|
||||
CMediaType::GetSampleSize() const {
|
||||
if (IsFixedSize()) {
|
||||
return lSampleSize;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetSampleSize(ULONG sz) {
|
||||
if (sz == 0) {
|
||||
SetVariableSize();
|
||||
} else {
|
||||
bFixedSizeSamples = TRUE;
|
||||
lSampleSize = sz;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetVariableSize() {
|
||||
bFixedSizeSamples = FALSE;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetTemporalCompression(BOOL bCompressed) {
|
||||
bTemporalCompression = bCompressed;
|
||||
}
|
||||
|
||||
BOOL
|
||||
CMediaType::SetFormat(BYTE * pformat, ULONG cb)
|
||||
{
|
||||
if (NULL == AllocFormatBuffer(cb))
|
||||
return(FALSE);
|
||||
|
||||
ASSERT(pbFormat);
|
||||
memcpy(pbFormat, pformat, cb);
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
|
||||
// set the type of the media type format block, this type defines what you
|
||||
// will actually find in the format pointer. For example FORMAT_VideoInfo or
|
||||
// FORMAT_WaveFormatEx. In the future this may be an interface pointer to a
|
||||
// property set. Before sending out media types this should be filled in.
|
||||
|
||||
void
|
||||
CMediaType::SetFormatType(const GUID *pformattype)
|
||||
{
|
||||
formattype = *pformattype;
|
||||
}
|
||||
|
||||
|
||||
// reset the format buffer
|
||||
|
||||
void CMediaType::ResetFormatBuffer()
|
||||
{
|
||||
if (cbFormat) {
|
||||
CoTaskMemFree((PVOID)pbFormat);
|
||||
}
|
||||
cbFormat = 0;
|
||||
pbFormat = NULL;
|
||||
}
|
||||
|
||||
|
||||
// allocate length bytes for the format and return a read/write pointer
|
||||
// If we cannot allocate the new block of memory we return NULL leaving
|
||||
// the original block of memory untouched (as does ReallocFormatBuffer)
|
||||
|
||||
BYTE*
|
||||
CMediaType::AllocFormatBuffer(ULONG length)
|
||||
{
|
||||
ASSERT(length);
|
||||
|
||||
// do the types have the same buffer size
|
||||
|
||||
if (cbFormat == length) {
|
||||
return pbFormat;
|
||||
}
|
||||
|
||||
// allocate the new format buffer
|
||||
|
||||
BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);
|
||||
if (pNewFormat == NULL) {
|
||||
if (length <= cbFormat) return pbFormat; //reuse the old block anyway.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// delete the old format
|
||||
|
||||
if (cbFormat != 0) {
|
||||
ASSERT(pbFormat);
|
||||
CoTaskMemFree((PVOID)pbFormat);
|
||||
}
|
||||
|
||||
cbFormat = length;
|
||||
pbFormat = pNewFormat;
|
||||
return pbFormat;
|
||||
}
|
||||
|
||||
|
||||
// reallocate length bytes for the format and return a read/write pointer
|
||||
// to it. We keep as much information as we can given the new buffer size
|
||||
// if this fails the original format buffer is left untouched. The caller
|
||||
// is responsible for ensuring the size of memory required is non zero
|
||||
|
||||
BYTE*
|
||||
CMediaType::ReallocFormatBuffer(ULONG length)
|
||||
{
|
||||
ASSERT(length);
|
||||
|
||||
// do the types have the same buffer size
|
||||
|
||||
if (cbFormat == length) {
|
||||
return pbFormat;
|
||||
}
|
||||
|
||||
// allocate the new format buffer
|
||||
|
||||
BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);
|
||||
if (pNewFormat == NULL) {
|
||||
if (length <= cbFormat) return pbFormat; //reuse the old block anyway.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// copy any previous format (or part of if new is smaller)
|
||||
// delete the old format and replace with the new one
|
||||
|
||||
if (cbFormat != 0) {
|
||||
ASSERT(pbFormat);
|
||||
memcpy(pNewFormat,pbFormat,min(length,cbFormat));
|
||||
CoTaskMemFree((PVOID)pbFormat);
|
||||
}
|
||||
|
||||
cbFormat = length;
|
||||
pbFormat = pNewFormat;
|
||||
return pNewFormat;
|
||||
}
|
||||
|
||||
// initialise a media type structure
|
||||
|
||||
void CMediaType::InitMediaType()
|
||||
{
|
||||
ZeroMemory((PVOID)this, sizeof(*this));
|
||||
lSampleSize = 1;
|
||||
bFixedSizeSamples = TRUE;
|
||||
}
|
||||
|
||||
|
||||
// a partially specified media type can be passed to IPin::Connect
|
||||
// as a constraint on the media type used in the connection.
|
||||
// the type, subtype or format type can be null.
|
||||
BOOL
|
||||
CMediaType::IsPartiallySpecified(void) const
|
||||
{
|
||||
if ((majortype == GUID_NULL) ||
|
||||
(formattype == GUID_NULL)) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL
|
||||
CMediaType::MatchesPartial(const CMediaType* ppartial) const
|
||||
{
|
||||
if ((ppartial->majortype != GUID_NULL) &&
|
||||
(majortype != ppartial->majortype)) {
|
||||
return FALSE;
|
||||
}
|
||||
if ((ppartial->subtype != GUID_NULL) &&
|
||||
(subtype != ppartial->subtype)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ppartial->formattype != GUID_NULL) {
|
||||
// if the format block is specified then it must match exactly
|
||||
if (formattype != ppartial->formattype) {
|
||||
return FALSE;
|
||||
}
|
||||
if (cbFormat != ppartial->cbFormat) {
|
||||
return FALSE;
|
||||
}
|
||||
if ((cbFormat != 0) &&
|
||||
(memcmp(pbFormat, ppartial->pbFormat, cbFormat) != 0)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// general purpose function to delete a heap allocated AM_MEDIA_TYPE structure
|
||||
// which is useful when calling IEnumMediaTypes::Next as the interface
|
||||
// implementation allocates the structures which you must later delete
|
||||
// the format block may also be a pointer to an interface to release
|
||||
|
||||
void WINAPI DeleteMediaType(AM_MEDIA_TYPE *pmt)
|
||||
{
|
||||
// allow NULL pointers for coding simplicity
|
||||
|
||||
if (pmt == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
FreeMediaType(*pmt);
|
||||
CoTaskMemFree((PVOID)pmt);
|
||||
}
|
||||
|
||||
|
||||
// this also comes in useful when using the IEnumMediaTypes interface so
|
||||
// that you can copy a media type, you can do nearly the same by creating
|
||||
// a CMediaType object but as soon as it goes out of scope the destructor
|
||||
// will delete the memory it allocated (this takes a copy of the memory)
|
||||
|
||||
AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc)
|
||||
{
|
||||
ASSERT(pSrc);
|
||||
|
||||
// Allocate a block of memory for the media type
|
||||
|
||||
AM_MEDIA_TYPE *pMediaType =
|
||||
(AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
|
||||
|
||||
if (pMediaType == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Copy the variable length format block
|
||||
|
||||
HRESULT hr = CopyMediaType(pMediaType,pSrc);
|
||||
if (FAILED(hr)) {
|
||||
CoTaskMemFree((PVOID)pMediaType);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pMediaType;
|
||||
}
|
||||
|
||||
|
||||
// Copy 1 media type to another
|
||||
|
||||
HRESULT WINAPI CopyMediaType(AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource)
|
||||
{
|
||||
// We'll leak if we copy onto one that already exists - there's one
|
||||
// case we can check like that - copying to itself.
|
||||
ASSERT(pmtSource != pmtTarget);
|
||||
*pmtTarget = *pmtSource;
|
||||
if (pmtSource->cbFormat != 0) {
|
||||
ASSERT(pmtSource->pbFormat != NULL);
|
||||
pmtTarget->pbFormat = (PBYTE)CoTaskMemAlloc(pmtSource->cbFormat);
|
||||
if (pmtTarget->pbFormat == NULL) {
|
||||
pmtTarget->cbFormat = 0;
|
||||
return E_OUTOFMEMORY;
|
||||
} else {
|
||||
CopyMemory((PVOID)pmtTarget->pbFormat, (PVOID)pmtSource->pbFormat,
|
||||
pmtTarget->cbFormat);
|
||||
}
|
||||
}
|
||||
if (pmtTarget->pUnk != NULL) {
|
||||
pmtTarget->pUnk->AddRef();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Free an existing media type (ie free resources it holds)
|
||||
|
||||
void WINAPI FreeMediaType(AM_MEDIA_TYPE& mt)
|
||||
{
|
||||
if (mt.cbFormat != 0) {
|
||||
CoTaskMemFree((PVOID)mt.pbFormat);
|
||||
|
||||
// Strictly unnecessary but tidier
|
||||
mt.cbFormat = 0;
|
||||
mt.pbFormat = NULL;
|
||||
}
|
||||
if (mt.pUnk != NULL) {
|
||||
mt.pUnk->Release();
|
||||
mt.pUnk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize a media type from a WAVEFORMATEX
|
||||
|
||||
STDAPI CreateAudioMediaType(
|
||||
const WAVEFORMATEX *pwfx,
|
||||
AM_MEDIA_TYPE *pmt,
|
||||
BOOL bSetFormat
|
||||
)
|
||||
{
|
||||
pmt->majortype = MEDIATYPE_Audio;
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
pmt->subtype = ((PWAVEFORMATEXTENSIBLE)pwfx)->SubFormat;
|
||||
} else {
|
||||
pmt->subtype = FOURCCMap(pwfx->wFormatTag);
|
||||
}
|
||||
pmt->formattype = FORMAT_WaveFormatEx;
|
||||
pmt->bFixedSizeSamples = TRUE;
|
||||
pmt->bTemporalCompression = FALSE;
|
||||
pmt->lSampleSize = pwfx->nBlockAlign;
|
||||
pmt->pUnk = NULL;
|
||||
if (bSetFormat) {
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {
|
||||
pmt->cbFormat = sizeof(WAVEFORMATEX);
|
||||
} else {
|
||||
pmt->cbFormat = sizeof(WAVEFORMATEX) + pwfx->cbSize;
|
||||
}
|
||||
pmt->pbFormat = (PBYTE)CoTaskMemAlloc(pmt->cbFormat);
|
||||
if (pmt->pbFormat == NULL) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {
|
||||
CopyMemory(pmt->pbFormat, pwfx, sizeof(PCMWAVEFORMAT));
|
||||
((WAVEFORMATEX *)pmt->pbFormat)->cbSize = 0;
|
||||
} else {
|
||||
CopyMemory(pmt->pbFormat, pwfx, pmt->cbFormat);
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// eliminate very many spurious warnings from MS compiler
|
||||
#pragma warning(disable:4514)
|
@ -1,89 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: MtType.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a class that holds and manages
|
||||
// media type information.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __MTYPE__
|
||||
#define __MTYPE__
|
||||
|
||||
/* Helper class that derived pin objects can use to compare media
|
||||
types etc. Has same data members as the struct AM_MEDIA_TYPE defined
|
||||
in the streams IDL file, but also has (non-virtual) functions */
|
||||
|
||||
class CMediaType : public _AMMediaType {
|
||||
|
||||
public:
|
||||
|
||||
~CMediaType();
|
||||
CMediaType();
|
||||
CMediaType(const GUID * majortype);
|
||||
CMediaType(const AM_MEDIA_TYPE&, HRESULT* phr = NULL);
|
||||
CMediaType(const CMediaType&, HRESULT* phr = NULL);
|
||||
|
||||
CMediaType& operator=(const CMediaType&);
|
||||
CMediaType& operator=(const AM_MEDIA_TYPE&);
|
||||
|
||||
BOOL operator == (const CMediaType&) const;
|
||||
BOOL operator != (const CMediaType&) const;
|
||||
|
||||
HRESULT Set(const CMediaType& rt);
|
||||
HRESULT Set(const AM_MEDIA_TYPE& rt);
|
||||
|
||||
BOOL IsValid() const;
|
||||
|
||||
const GUID *Type() const { return &majortype;} ;
|
||||
void SetType(const GUID *);
|
||||
const GUID *Subtype() const { return &subtype;} ;
|
||||
void SetSubtype(const GUID *);
|
||||
|
||||
BOOL IsFixedSize() const {return bFixedSizeSamples; };
|
||||
BOOL IsTemporalCompressed() const {return bTemporalCompression; };
|
||||
ULONG GetSampleSize() const;
|
||||
|
||||
void SetSampleSize(ULONG sz);
|
||||
void SetVariableSize();
|
||||
void SetTemporalCompression(BOOL bCompressed);
|
||||
|
||||
// read/write pointer to format - can't change length without
|
||||
// calling SetFormat, AllocFormatBuffer or ReallocFormatBuffer
|
||||
|
||||
BYTE* Format() const {return pbFormat; };
|
||||
ULONG FormatLength() const { return cbFormat; };
|
||||
|
||||
void SetFormatType(const GUID *);
|
||||
const GUID *FormatType() const {return &formattype; };
|
||||
BOOL SetFormat(BYTE *pFormat, ULONG length);
|
||||
void ResetFormatBuffer();
|
||||
BYTE* AllocFormatBuffer(ULONG length);
|
||||
BYTE* ReallocFormatBuffer(ULONG length);
|
||||
|
||||
void InitMediaType();
|
||||
|
||||
BOOL MatchesPartial(const CMediaType* ppartial) const;
|
||||
BOOL IsPartiallySpecified(void) const;
|
||||
};
|
||||
|
||||
|
||||
/* General purpose functions to copy and delete a task allocated AM_MEDIA_TYPE
|
||||
structure which is useful when using the IEnumMediaFormats interface as
|
||||
the implementation allocates the structures which you must later delete */
|
||||
|
||||
void WINAPI DeleteMediaType(AM_MEDIA_TYPE *pmt);
|
||||
AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc);
|
||||
HRESULT WINAPI CopyMediaType(AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource);
|
||||
void WINAPI FreeMediaType(AM_MEDIA_TYPE& mt);
|
||||
|
||||
// Initialize a media type from a WAVEFORMATEX
|
||||
|
||||
STDAPI CreateAudioMediaType(
|
||||
const WAVEFORMATEX *pwfx,
|
||||
AM_MEDIA_TYPE *pmt,
|
||||
BOOL bSetFormat);
|
||||
|
||||
#endif /* __MTYPE__ */
|
||||
|
@ -1,794 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: OutputQ.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements COutputQueue class used by an
|
||||
// output pin which may sometimes want to queue output samples on a
|
||||
// separate thread and sometimes call Receive() directly on the input
|
||||
// pin.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#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,
|
||||
bool bFlushingOpt // flushing optimization
|
||||
) : m_lBatchSize(lBatchSize),
|
||||
m_bBatchExact(bBatchExact && (lBatchSize > 1)),
|
||||
m_hThread(NULL),
|
||||
m_hSem(NULL),
|
||||
m_List(NULL),
|
||||
m_pPin(pInputPin),
|
||||
m_ppSamples(NULL),
|
||||
m_lWaiting(0),
|
||||
m_pInputPin(NULL),
|
||||
m_bSendAnyway(FALSE),
|
||||
m_nBatched(0),
|
||||
m_bFlushing(FALSE),
|
||||
m_bFlushed(TRUE),
|
||||
m_bFlushingOpt(bFlushingOpt),
|
||||
m_bTerminate(FALSE),
|
||||
m_hEventPop(NULL),
|
||||
m_hr(S_OK)
|
||||
{
|
||||
ASSERT(m_lBatchSize > 0);
|
||||
|
||||
|
||||
if (FAILED(*phr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the input pin is OK and cache its IMemInputPin interface
|
||||
|
||||
*phr = pInputPin->QueryInterface(IID_IMemInputPin, (void **)&m_pInputPin);
|
||||
if (FAILED(*phr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// See if we should ask the downstream pin
|
||||
|
||||
if (bAuto) {
|
||||
HRESULT hr = m_pInputPin->ReceiveCanBlock();
|
||||
if (SUCCEEDED(hr)) {
|
||||
bQueue = hr == S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Create our sample batch
|
||||
|
||||
m_ppSamples = new PMEDIASAMPLE[m_lBatchSize];
|
||||
if (m_ppSamples == NULL) {
|
||||
*phr = E_OUTOFMEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're queueing allocate resources
|
||||
|
||||
if (bQueue) {
|
||||
DbgLog((LOG_TRACE, 2, TEXT("Creating thread for output pin")));
|
||||
m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
|
||||
if (m_hSem == NULL) {
|
||||
DWORD dwError = GetLastError();
|
||||
*phr = AmHresultFromWin32(dwError);
|
||||
return;
|
||||
}
|
||||
m_List = new CSampleList(NAME("Sample Queue List"),
|
||||
lListSize,
|
||||
FALSE // No lock
|
||||
);
|
||||
if (m_List == NULL) {
|
||||
*phr = E_OUTOFMEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DWORD dwThreadId;
|
||||
m_hThread = CreateThread(NULL,
|
||||
0,
|
||||
InitialThreadProc,
|
||||
(LPVOID)this,
|
||||
0,
|
||||
&dwThreadId);
|
||||
if (m_hThread == NULL) {
|
||||
DWORD dwError = GetLastError();
|
||||
*phr = AmHresultFromWin32(dwError);
|
||||
return;
|
||||
}
|
||||
SetThreadPriority(m_hThread, dwPriority);
|
||||
} else {
|
||||
DbgLog((LOG_TRACE, 2, TEXT("Calling input pin directly - no thread")));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// COutputQueuee Destructor :
|
||||
//
|
||||
// Free all resources -
|
||||
//
|
||||
// Thread,
|
||||
// Batched samples
|
||||
//
|
||||
COutputQueue::~COutputQueue()
|
||||
{
|
||||
DbgLog((LOG_TRACE, 3, TEXT("COutputQueue::~COutputQueue")));
|
||||
/* Free our pointer */
|
||||
if (m_pInputPin != NULL) {
|
||||
m_pInputPin->Release();
|
||||
}
|
||||
if (m_hThread != NULL) {
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
m_bTerminate = TRUE;
|
||||
m_hr = S_FALSE;
|
||||
NotifyThread();
|
||||
}
|
||||
DbgWaitForSingleObject(m_hThread);
|
||||
EXECUTE_ASSERT(CloseHandle(m_hThread));
|
||||
|
||||
// The thread frees the samples when asked to terminate
|
||||
|
||||
ASSERT(m_List->GetCount() == 0);
|
||||
delete m_List;
|
||||
} else {
|
||||
FreeSamples();
|
||||
}
|
||||
if (m_hSem != NULL) {
|
||||
EXECUTE_ASSERT(CloseHandle(m_hSem));
|
||||
}
|
||||
delete [] m_ppSamples;
|
||||
}
|
||||
|
||||
//
|
||||
// Call the real thread proc as a member function
|
||||
//
|
||||
DWORD WINAPI COutputQueue::InitialThreadProc(LPVOID pv)
|
||||
{
|
||||
HRESULT hrCoInit = CAMThread::CoInitializeHelper();
|
||||
|
||||
COutputQueue *pSampleQueue = (COutputQueue *)pv;
|
||||
DWORD dwReturn = pSampleQueue->ThreadProc();
|
||||
|
||||
if(hrCoInit == S_OK) {
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
return dwReturn;
|
||||
}
|
||||
|
||||
//
|
||||
// Thread sending the samples downstream :
|
||||
//
|
||||
// When there is nothing to do the thread sets m_lWaiting (while
|
||||
// holding the critical section) and then waits for m_hSem to be
|
||||
// set (not holding the critical section)
|
||||
//
|
||||
DWORD COutputQueue::ThreadProc()
|
||||
{
|
||||
while (TRUE) {
|
||||
BOOL bWait = FALSE;
|
||||
IMediaSample *pSample;
|
||||
LONG lNumberToSend; // Local copy
|
||||
NewSegmentPacket* ppacket;
|
||||
|
||||
//
|
||||
// Get a batch of samples and send it if possible
|
||||
// In any case exit the loop if there is a control action
|
||||
// requested
|
||||
//
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
while (TRUE) {
|
||||
|
||||
if (m_bTerminate) {
|
||||
FreeSamples();
|
||||
return 0;
|
||||
}
|
||||
if (m_bFlushing) {
|
||||
FreeSamples();
|
||||
SetEvent(m_evFlushComplete);
|
||||
}
|
||||
|
||||
// Get a sample off the list
|
||||
|
||||
pSample = m_List->RemoveHead();
|
||||
// inform derived class we took something off the queue
|
||||
if (m_hEventPop) {
|
||||
//DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
|
||||
SetEvent(m_hEventPop);
|
||||
}
|
||||
|
||||
if (pSample != NULL &&
|
||||
!IsSpecialSample(pSample)) {
|
||||
|
||||
// If its just a regular sample just add it to the batch
|
||||
// and exit the loop if the batch is full
|
||||
|
||||
m_ppSamples[m_nBatched++] = pSample;
|
||||
if (m_nBatched == m_lBatchSize) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
||||
// If there was nothing in the queue and there's nothing
|
||||
// to send (either because there's nothing or the batch
|
||||
// isn't full) then prepare to wait
|
||||
|
||||
if (pSample == NULL &&
|
||||
(m_bBatchExact || m_nBatched == 0)) {
|
||||
|
||||
// Tell other thread to set the event when there's
|
||||
// something do to
|
||||
|
||||
ASSERT(m_lWaiting == 0);
|
||||
m_lWaiting++;
|
||||
bWait = TRUE;
|
||||
} else {
|
||||
|
||||
// We break out of the loop on SEND_PACKET unless
|
||||
// there's nothing to send
|
||||
|
||||
if (pSample == SEND_PACKET && m_nBatched == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pSample == NEW_SEGMENT) {
|
||||
// now we need the parameters - we are
|
||||
// guaranteed that the next packet contains them
|
||||
ppacket = (NewSegmentPacket *) m_List->RemoveHead();
|
||||
// we took something off the queue
|
||||
if (m_hEventPop) {
|
||||
//DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
|
||||
SetEvent(m_hEventPop);
|
||||
}
|
||||
|
||||
ASSERT(ppacket);
|
||||
}
|
||||
// EOS_PACKET falls through here and we exit the loop
|
||||
// In this way it acts like SEND_PACKET
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!bWait) {
|
||||
// We look at m_nBatched from the client side so keep
|
||||
// it up to date inside the critical section
|
||||
lNumberToSend = m_nBatched; // Local copy
|
||||
m_nBatched = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for some more data
|
||||
|
||||
if (bWait) {
|
||||
DbgWaitForSingleObject(m_hSem);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// OK - send it if there's anything to send
|
||||
// We DON'T check m_bBatchExact here because either we've got
|
||||
// a full batch or we dropped through because we got
|
||||
// SEND_PACKET or EOS_PACKET - both of which imply we should
|
||||
// flush our batch
|
||||
|
||||
if (lNumberToSend != 0) {
|
||||
long nProcessed;
|
||||
if (m_hr == S_OK) {
|
||||
ASSERT(!m_bFlushed);
|
||||
HRESULT hr = m_pInputPin->ReceiveMultiple(m_ppSamples,
|
||||
lNumberToSend,
|
||||
&nProcessed);
|
||||
/* Don't overwrite a flushing state HRESULT */
|
||||
CAutoLock lck(this);
|
||||
if (m_hr == S_OK) {
|
||||
m_hr = hr;
|
||||
}
|
||||
ASSERT(!m_bFlushed);
|
||||
}
|
||||
while (lNumberToSend != 0) {
|
||||
m_ppSamples[--lNumberToSend]->Release();
|
||||
}
|
||||
if (m_hr != S_OK) {
|
||||
|
||||
// In any case wait for more data - S_OK just
|
||||
// means there wasn't an error
|
||||
|
||||
DbgLog((LOG_ERROR, 2, TEXT("ReceiveMultiple returned %8.8X"),
|
||||
m_hr));
|
||||
}
|
||||
}
|
||||
|
||||
// Check for end of stream
|
||||
|
||||
if (pSample == EOS_PACKET) {
|
||||
|
||||
// We don't send even end of stream on if we've previously
|
||||
// returned something other than S_OK
|
||||
// This is because in that case the pin which returned
|
||||
// something other than S_OK should have either sent
|
||||
// EndOfStream() or notified the filter graph
|
||||
|
||||
if (m_hr == S_OK) {
|
||||
DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));
|
||||
HRESULT hr = m_pPin->EndOfStream();
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Data from a new source
|
||||
|
||||
if (pSample == RESET_PACKET) {
|
||||
m_hr = S_OK;
|
||||
SetEvent(m_evFlushComplete);
|
||||
}
|
||||
|
||||
if (pSample == NEW_SEGMENT) {
|
||||
m_pPin->NewSegment(ppacket->tStart, ppacket->tStop, ppacket->dRate);
|
||||
delete ppacket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send batched stuff anyway
|
||||
void COutputQueue::SendAnyway()
|
||||
{
|
||||
if (!IsQueued()) {
|
||||
|
||||
// m_bSendAnyway is a private parameter checked in ReceiveMultiple
|
||||
|
||||
m_bSendAnyway = TRUE;
|
||||
LONG nProcessed;
|
||||
ReceiveMultiple(NULL, 0, &nProcessed);
|
||||
m_bSendAnyway = FALSE;
|
||||
|
||||
} else {
|
||||
CAutoLock lck(this);
|
||||
QueueSample(SEND_PACKET);
|
||||
NotifyThread();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
COutputQueue::NewSegment(
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop,
|
||||
double dRate)
|
||||
{
|
||||
if (!IsQueued()) {
|
||||
if (S_OK == m_hr) {
|
||||
if (m_bBatchExact) {
|
||||
SendAnyway();
|
||||
}
|
||||
m_pPin->NewSegment(tStart, tStop, dRate);
|
||||
}
|
||||
} else {
|
||||
if (m_hr == S_OK) {
|
||||
//
|
||||
// we need to queue the new segment to appear in order in the
|
||||
// data, but we need to pass parameters to it. Rather than
|
||||
// take the hit of wrapping every single sample so we can tell
|
||||
// special ones apart, we queue special pointers to indicate
|
||||
// special packets, and we guarantee (by holding the
|
||||
// critical section) that the packet immediately following a
|
||||
// NEW_SEGMENT value is a NewSegmentPacket containing the
|
||||
// parameters.
|
||||
NewSegmentPacket * ppack = new NewSegmentPacket;
|
||||
if (ppack == NULL) {
|
||||
return;
|
||||
}
|
||||
ppack->tStart = tStart;
|
||||
ppack->tStop = tStop;
|
||||
ppack->dRate = dRate;
|
||||
|
||||
CAutoLock lck(this);
|
||||
QueueSample(NEW_SEGMENT);
|
||||
QueueSample( (IMediaSample*) ppack);
|
||||
NotifyThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// End of Stream is queued to output device
|
||||
//
|
||||
void COutputQueue::EOS()
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
if (!IsQueued()) {
|
||||
if (m_bBatchExact) {
|
||||
SendAnyway();
|
||||
}
|
||||
if (m_hr == S_OK) {
|
||||
DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));
|
||||
m_bFlushed = FALSE;
|
||||
HRESULT hr = m_pPin->EndOfStream();
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_hr == S_OK) {
|
||||
m_bFlushed = FALSE;
|
||||
QueueSample(EOS_PACKET);
|
||||
NotifyThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Flush all the samples in the queue
|
||||
//
|
||||
void COutputQueue::BeginFlush()
|
||||
{
|
||||
if (IsQueued()) {
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
|
||||
// block receives -- we assume this is done by the
|
||||
// filter in which we are a component
|
||||
|
||||
// discard all queued data
|
||||
|
||||
m_bFlushing = TRUE;
|
||||
|
||||
// Make sure we discard all samples from now on
|
||||
|
||||
if (m_hr == S_OK) {
|
||||
m_hr = S_FALSE;
|
||||
}
|
||||
|
||||
// Optimize so we don't keep calling downstream all the time
|
||||
|
||||
if (m_bFlushed && m_bFlushingOpt) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we really wait for the flush to complete
|
||||
m_evFlushComplete.Reset();
|
||||
|
||||
NotifyThread();
|
||||
}
|
||||
|
||||
// pass this downstream
|
||||
|
||||
m_pPin->BeginFlush();
|
||||
} else {
|
||||
// pass downstream first to avoid deadlocks
|
||||
m_pPin->BeginFlush();
|
||||
CAutoLock lck(this);
|
||||
// discard all queued data
|
||||
|
||||
m_bFlushing = TRUE;
|
||||
|
||||
// Make sure we discard all samples from now on
|
||||
|
||||
if (m_hr == S_OK) {
|
||||
m_hr = S_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// leave flush mode - pass this downstream
|
||||
void COutputQueue::EndFlush()
|
||||
{
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
ASSERT(m_bFlushing);
|
||||
if (m_bFlushingOpt && m_bFlushed && IsQueued()) {
|
||||
m_bFlushing = FALSE;
|
||||
m_hr = S_OK;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// sync with pushing thread -- done in BeginFlush
|
||||
// ensure no more data to go downstream -- done in BeginFlush
|
||||
//
|
||||
// Because we are synching here there is no need to hold the critical
|
||||
// section (in fact we'd deadlock if we did!)
|
||||
|
||||
if (IsQueued()) {
|
||||
m_evFlushComplete.Wait();
|
||||
} else {
|
||||
FreeSamples();
|
||||
}
|
||||
|
||||
// Be daring - the caller has guaranteed no samples will arrive
|
||||
// before EndFlush() returns
|
||||
|
||||
m_bFlushing = FALSE;
|
||||
m_bFlushed = TRUE;
|
||||
|
||||
// call EndFlush on downstream pins
|
||||
|
||||
m_pPin->EndFlush();
|
||||
|
||||
m_hr = S_OK;
|
||||
}
|
||||
|
||||
// COutputQueue::QueueSample
|
||||
//
|
||||
// private method to Send a sample to the output queue
|
||||
// The critical section MUST be held when this is called
|
||||
|
||||
void COutputQueue::QueueSample(IMediaSample *pSample)
|
||||
{
|
||||
if (NULL == m_List->AddTail(pSample)) {
|
||||
if (!IsSpecialSample(pSample)) {
|
||||
pSample->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// COutputQueue::Receive()
|
||||
//
|
||||
// Send a single sample by the multiple sample route
|
||||
// (NOTE - this could be optimized if necessary)
|
||||
//
|
||||
// On return the sample will have been Release()'d
|
||||
//
|
||||
|
||||
HRESULT COutputQueue::Receive(IMediaSample *pSample)
|
||||
{
|
||||
LONG nProcessed;
|
||||
return ReceiveMultiple(&pSample, 1, &nProcessed);
|
||||
}
|
||||
|
||||
//
|
||||
// COutputQueue::ReceiveMultiple()
|
||||
//
|
||||
// Send a set of samples to the downstream pin
|
||||
//
|
||||
// ppSamples - array of samples
|
||||
// nSamples - how many
|
||||
// nSamplesProcessed - How many were processed
|
||||
//
|
||||
// On return all samples will have been Release()'d
|
||||
//
|
||||
|
||||
HRESULT COutputQueue::ReceiveMultiple (
|
||||
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;
|
||||
long iDone;
|
||||
for (iDone = 0;
|
||||
iDone < nSamples || (m_nBatched != 0 && m_bSendAnyway);
|
||||
) {
|
||||
|
||||
//pragma message (REMIND("Implement threshold scheme"))
|
||||
ASSERT(m_nBatched < m_lBatchSize);
|
||||
if (iDone < nSamples) {
|
||||
m_ppSamples[m_nBatched++] = ppSamples[iDone++];
|
||||
}
|
||||
if (m_nBatched == m_lBatchSize ||
|
||||
nSamples == 0 && (m_bSendAnyway || !m_bBatchExact)) {
|
||||
LONG nDone;
|
||||
DbgLog((LOG_TRACE, 4, TEXT("Batching %d samples"),
|
||||
m_nBatched));
|
||||
|
||||
if (m_hr == S_OK) {
|
||||
m_hr = m_pInputPin->ReceiveMultiple(m_ppSamples,
|
||||
m_nBatched,
|
||||
&nDone);
|
||||
} else {
|
||||
nDone = 0;
|
||||
}
|
||||
iLost += m_nBatched - nDone;
|
||||
for (LONG i = 0; i < m_nBatched; i++) {
|
||||
m_ppSamples[i]->Release();
|
||||
}
|
||||
m_nBatched = 0;
|
||||
}
|
||||
}
|
||||
*nSamplesProcessed = iDone - iLost;
|
||||
if (*nSamplesProcessed < 0) {
|
||||
*nSamplesProcessed = 0;
|
||||
}
|
||||
return m_hr;
|
||||
} else {
|
||||
/* We're sending to our thread */
|
||||
|
||||
if (m_hr != S_OK) {
|
||||
*nSamplesProcessed = 0;
|
||||
DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (queued) : Discarding %d samples code 0x%8.8X"),
|
||||
nSamples, m_hr));
|
||||
for (int i = 0; i < nSamples; i++) {
|
||||
ppSamples[i]->Release();
|
||||
}
|
||||
return m_hr;
|
||||
}
|
||||
m_bFlushed = FALSE;
|
||||
for (long i = 0; i < nSamples; i++) {
|
||||
QueueSample(ppSamples[i]);
|
||||
}
|
||||
*nSamplesProcessed = nSamples;
|
||||
if (!m_bBatchExact ||
|
||||
m_nBatched + m_List->GetCount() >= m_lBatchSize) {
|
||||
NotifyThread();
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Get ready for new data - cancels sticky m_hr
|
||||
void COutputQueue::Reset()
|
||||
{
|
||||
if (!IsQueued()) {
|
||||
m_hr = S_OK;
|
||||
} else {
|
||||
CAutoLock lck(this);
|
||||
QueueSample(RESET_PACKET);
|
||||
NotifyThread();
|
||||
m_evFlushComplete.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove and Release() all queued and Batched samples
|
||||
void COutputQueue::FreeSamples()
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
if (IsQueued()) {
|
||||
while (TRUE) {
|
||||
IMediaSample *pSample = m_List->RemoveHead();
|
||||
// inform derived class we took something off the queue
|
||||
if (m_hEventPop) {
|
||||
//DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
|
||||
SetEvent(m_hEventPop);
|
||||
}
|
||||
|
||||
if (pSample == NULL) {
|
||||
break;
|
||||
}
|
||||
if (!IsSpecialSample(pSample)) {
|
||||
pSample->Release();
|
||||
} else {
|
||||
if (pSample == NEW_SEGMENT) {
|
||||
// Free NEW_SEGMENT packet
|
||||
NewSegmentPacket *ppacket =
|
||||
(NewSegmentPacket *) m_List->RemoveHead();
|
||||
// inform derived class we took something off the queue
|
||||
if (m_hEventPop) {
|
||||
//DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered SET EVENT")));
|
||||
SetEvent(m_hEventPop);
|
||||
}
|
||||
|
||||
ASSERT(ppacket != NULL);
|
||||
delete ppacket;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < m_nBatched; i++) {
|
||||
m_ppSamples[i]->Release();
|
||||
}
|
||||
m_nBatched = 0;
|
||||
}
|
||||
|
||||
// Notify the thread if there is something to do
|
||||
//
|
||||
// The critical section MUST be held when this is called
|
||||
void COutputQueue::NotifyThread()
|
||||
{
|
||||
// Optimize - no need to signal if it's not waiting
|
||||
ASSERT(IsQueued());
|
||||
if (m_lWaiting) {
|
||||
ReleaseSemaphore(m_hSem, m_lWaiting, NULL);
|
||||
m_lWaiting = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// See if there's any work to do
|
||||
// Returns
|
||||
// TRUE if there is nothing on the queue and nothing in the batch
|
||||
// and all data has been sent
|
||||
// FALSE otherwise
|
||||
//
|
||||
BOOL COutputQueue::IsIdle()
|
||||
{
|
||||
CAutoLock lck(this);
|
||||
|
||||
// We're idle if
|
||||
// there is no thread (!IsQueued()) OR
|
||||
// the thread is waiting for more work (m_lWaiting != 0)
|
||||
// AND
|
||||
// there's nothing in the current batch (m_nBatched == 0)
|
||||
|
||||
if (IsQueued() && m_lWaiting == 0 || m_nBatched != 0) {
|
||||
return FALSE;
|
||||
} else {
|
||||
|
||||
// If we're idle it shouldn't be possible for there
|
||||
// to be anything on the work queue
|
||||
|
||||
ASSERT(!IsQueued() || m_List->GetCount() == 0);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void COutputQueue::SetPopEvent(HANDLE hEvent)
|
||||
{
|
||||
m_hEventPop = hEvent;
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: OutputQ.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines the COutputQueue class, which
|
||||
// makes a queue of samples and sends them to an output pin. The
|
||||
// class will optionally send the samples to the pin directly.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
typedef CGenericList<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,
|
||||
bool bFlushingOpt = false // flushing optimization
|
||||
);
|
||||
~COutputQueue();
|
||||
|
||||
// enter flush state - discard all data
|
||||
void BeginFlush(); // Begin flushing samples
|
||||
|
||||
// re-enable receives (pass this downstream)
|
||||
void EndFlush(); // Complete flush of samples - downstream
|
||||
// pin guaranteed not to block at this stage
|
||||
|
||||
void EOS(); // Call this on End of stream
|
||||
|
||||
void SendAnyway(); // Send batched samples anyway (if bBatchExact set)
|
||||
|
||||
void NewSegment(
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop,
|
||||
double dRate);
|
||||
|
||||
HRESULT Receive(IMediaSample *pSample);
|
||||
|
||||
// do something with these media samples
|
||||
HRESULT ReceiveMultiple (
|
||||
IMediaSample **pSamples,
|
||||
long nSamples,
|
||||
long *nSamplesProcessed);
|
||||
|
||||
void Reset(); // Reset m_hr ready for more data
|
||||
|
||||
// See if its idle or not
|
||||
BOOL IsIdle();
|
||||
|
||||
// give the class an event to fire after everything removed from the queue
|
||||
void SetPopEvent(HANDLE hEvent);
|
||||
|
||||
protected:
|
||||
static DWORD WINAPI InitialThreadProc(LPVOID pv);
|
||||
DWORD ThreadProc();
|
||||
BOOL IsQueued()
|
||||
{
|
||||
return m_List != NULL;
|
||||
}
|
||||
|
||||
// The critical section MUST be held when this is called
|
||||
void QueueSample(IMediaSample *pSample);
|
||||
|
||||
BOOL IsSpecialSample(IMediaSample *pSample)
|
||||
{
|
||||
return (DWORD_PTR)pSample > (DWORD_PTR)(LONG_PTR)(-16);
|
||||
}
|
||||
|
||||
// Remove and Release() batched and queued samples
|
||||
void FreeSamples();
|
||||
|
||||
// Notify the thread there is something to do
|
||||
void NotifyThread();
|
||||
|
||||
|
||||
protected:
|
||||
// Queue 'messages'
|
||||
#define SEND_PACKET ((IMediaSample *)(LONG_PTR)(-2)) // Send batch
|
||||
#define EOS_PACKET ((IMediaSample *)(LONG_PTR)(-3)) // End of stream
|
||||
#define RESET_PACKET ((IMediaSample *)(LONG_PTR)(-4)) // Reset m_hr
|
||||
#define NEW_SEGMENT ((IMediaSample *)(LONG_PTR)(-5)) // send NewSegment
|
||||
|
||||
// new segment packet is always followed by one of these
|
||||
struct NewSegmentPacket {
|
||||
REFERENCE_TIME tStart;
|
||||
REFERENCE_TIME tStop;
|
||||
double dRate;
|
||||
};
|
||||
|
||||
// Remember input stuff
|
||||
IPin * const m_pPin;
|
||||
IMemInputPin * m_pInputPin;
|
||||
BOOL const m_bBatchExact;
|
||||
LONG const m_lBatchSize;
|
||||
|
||||
CSampleList * m_List;
|
||||
HANDLE m_hSem;
|
||||
CAMEvent m_evFlushComplete;
|
||||
HANDLE m_hThread;
|
||||
IMediaSample ** m_ppSamples;
|
||||
LONG m_nBatched;
|
||||
|
||||
// Wait optimization
|
||||
LONG m_lWaiting;
|
||||
// Flush synchronization
|
||||
BOOL m_bFlushing;
|
||||
|
||||
// flushing optimization. some downstream filters have trouble
|
||||
// with the queue's flushing optimization. other rely on it
|
||||
BOOL m_bFlushed;
|
||||
bool m_bFlushingOpt;
|
||||
|
||||
// Terminate now
|
||||
BOOL m_bTerminate;
|
||||
|
||||
// Send anyway flag for batching
|
||||
BOOL m_bSendAnyway;
|
||||
|
||||
// Deferred 'return code'
|
||||
BOOL volatile m_hr;
|
||||
|
||||
// an event that can be fired after every deliver
|
||||
HANDLE m_hEventPop;
|
||||
};
|
||||
|
@ -1,196 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: PStream.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include "streams.h"
|
||||
|
||||
#ifdef PERF
|
||||
#include <measure.h>
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
// 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
|
||||
(void)StringCchPrintfW(Buff, NUMELMS(Buff), L"%011d ",n);
|
||||
return pIStream->Write(&(Buff[0]), 12*sizeof(WCHAR), NULL);
|
||||
} // WriteInt
|
||||
|
||||
|
||||
// ReadInt
|
||||
//
|
||||
// Reads an integer from an IStream.
|
||||
// Read as 4 bytes. You could use this for shorts or unsigneds or anything
|
||||
// where the value isn't actually truncated by squeezing it into 32 bits
|
||||
// Striped down subset of what sscanf can do (without dragging in the C runtime)
|
||||
|
||||
STDAPI_(int) ReadInt(IStream *pIStream, 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)
|
@ -1,114 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: PStream.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a class for persistent properties
|
||||
// of filters.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __PSTREAM__
|
||||
#define __PSTREAM__
|
||||
|
||||
// Base class for persistent properties of filters
|
||||
// (i.e. filter properties in saved graphs)
|
||||
|
||||
// The simplest way to use this is:
|
||||
// 1. Arrange for your filter to inherit this class
|
||||
// 2. Implement in your class WriteToStream and ReadFromStream
|
||||
// These will override the "do nothing" functions here.
|
||||
// 3. Change your NonDelegatingQueryInterface to handle IPersistStream
|
||||
// 4. Implement SizeMax to return the number of bytes of data you save.
|
||||
// If you save UNICODE data, don't forget a char is 2 bytes.
|
||||
// 5. Whenever your data changes, call SetDirty()
|
||||
//
|
||||
// At some point you may decide to alter, or extend the format of your data.
|
||||
// At that point you will wish that you had a version number in all the old
|
||||
// saved graphs, so that you can tell, when you read them, whether they
|
||||
// represent the old or new form. To assist you in this, this class
|
||||
// writes and reads a version number.
|
||||
// When it writes, it calls GetSoftwareVersion() to enquire what version
|
||||
// of the software we have at the moment. (In effect this is a version number
|
||||
// of the data layout in the file). It writes this as the first thing in the data.
|
||||
// If you want to change the version, implement (override) GetSoftwareVersion().
|
||||
// It reads this from the file into mPS_dwFileVersion before calling ReadFromStream,
|
||||
// so in ReadFromStream you can check mPS_dwFileVersion to see if you are reading
|
||||
// an old version file.
|
||||
// Normally you should accept files whose version is no newer than the software
|
||||
// version that's reading them.
|
||||
|
||||
|
||||
// CPersistStream
|
||||
//
|
||||
// Implements IPersistStream.
|
||||
// See 'OLE Programmers Reference (Vol 1):Structured Storage Overview' for
|
||||
// more implementation information.
|
||||
class CPersistStream : public IPersistStream {
|
||||
private:
|
||||
|
||||
// Internal state:
|
||||
|
||||
protected:
|
||||
DWORD mPS_dwFileVersion; // version number of file (being read)
|
||||
BOOL mPS_fDirty;
|
||||
|
||||
public:
|
||||
|
||||
// IPersistStream methods
|
||||
|
||||
STDMETHODIMP IsDirty()
|
||||
{return (mPS_fDirty ? S_OK : S_FALSE);} // note FALSE means clean
|
||||
STDMETHODIMP Load(LPSTREAM pStm);
|
||||
STDMETHODIMP Save(LPSTREAM pStm, BOOL fClearDirty);
|
||||
STDMETHODIMP GetSizeMax(ULARGE_INTEGER * pcbSize)
|
||||
// Allow 24 bytes for version.
|
||||
{ pcbSize->QuadPart = 12*sizeof(WCHAR)+SizeMax(); return NOERROR; }
|
||||
|
||||
// implementation
|
||||
|
||||
CPersistStream(IUnknown *punk, HRESULT *phr);
|
||||
~CPersistStream();
|
||||
|
||||
HRESULT SetDirty(BOOL fDirty)
|
||||
{ mPS_fDirty = fDirty; return NOERROR;}
|
||||
|
||||
|
||||
// override to reveal IPersist & IPersistStream
|
||||
// STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
|
||||
|
||||
// --- IPersist ---
|
||||
|
||||
// You must override this to provide your own class id
|
||||
STDMETHODIMP GetClassID(CLSID *pClsid) PURE;
|
||||
|
||||
// overrideable if you want
|
||||
// file version number. Override it if you ever change format
|
||||
virtual DWORD GetSoftwareVersion(void) { return 0; }
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// OVERRIDE THESE to read and write your data
|
||||
// OVERRIDE THESE to read and write your data
|
||||
// OVERRIDE THESE to read and write your data
|
||||
|
||||
virtual int SizeMax() {return 0;}
|
||||
virtual HRESULT WriteToStream(IStream *pStream);
|
||||
virtual HRESULT ReadFromStream(IStream *pStream);
|
||||
//=========================================================================
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
// --- Useful helpers ---
|
||||
|
||||
|
||||
// Writes an int to an IStream as UNICODE.
|
||||
STDAPI WriteInt(IStream *pIStream, int n);
|
||||
|
||||
// inverse of WriteInt
|
||||
STDAPI_(int) ReadInt(IStream *pIStream, HRESULT &hr);
|
||||
|
||||
#endif // __PSTREAM__
|
@ -1,527 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: PullPin.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements CPullPin class that pulls data
|
||||
// from IAsyncReader.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#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_PTR dwUnused;
|
||||
HRESULT hr = m_pReader->WaitForNext(
|
||||
INFINITE,
|
||||
&pSample,
|
||||
&dwUnused);
|
||||
if (FAILED(hr)) {
|
||||
if (pSample) {
|
||||
pSample->Release();
|
||||
}
|
||||
} else {
|
||||
hr = DeliverSample(pSample, tStart, tStop);
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
CleanupCancelled();
|
||||
OnError(hr);
|
||||
}
|
||||
return hr;
|
||||
|
||||
}
|
||||
|
||||
HRESULT
|
||||
CPullPin::DeliverSample(
|
||||
IMediaSample* pSample,
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop
|
||||
)
|
||||
{
|
||||
// fix up sample if past actual stop (for sector alignment)
|
||||
REFERENCE_TIME t1, t2;
|
||||
pSample->GetTime(&t1, &t2);
|
||||
if (t2 > tStop) {
|
||||
t2 = tStop;
|
||||
}
|
||||
|
||||
// adjust times to be relative to (aligned) start time
|
||||
t1 -= tStart;
|
||||
t2 -= tStart;
|
||||
pSample->SetTime(&t1, &t2);
|
||||
|
||||
|
||||
HRESULT hr = Receive(pSample);
|
||||
pSample->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
void
|
||||
CPullPin::Process(void)
|
||||
{
|
||||
// is there anything to do?
|
||||
if (m_tStop <= m_tStart) {
|
||||
EndOfStream();
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL bDiscontinuity = TRUE;
|
||||
|
||||
// if there is more than one sample at the allocator,
|
||||
// then try to queue 2 at once in order to overlap.
|
||||
// -- get buffer count and required alignment
|
||||
ALLOCATOR_PROPERTIES Actual;
|
||||
HRESULT hr = m_pAlloc->GetProperties(&Actual);
|
||||
|
||||
// align the start position downwards
|
||||
REFERENCE_TIME tStart = AlignDown(m_tStart / UNITS, Actual.cbAlign) * UNITS;
|
||||
REFERENCE_TIME tCurrent = tStart;
|
||||
|
||||
REFERENCE_TIME tStop = m_tStop;
|
||||
if (tStop > m_tDuration) {
|
||||
tStop = m_tDuration;
|
||||
}
|
||||
|
||||
// align the stop position - may be past stop, but that
|
||||
// doesn't matter
|
||||
REFERENCE_TIME tAlignStop = AlignUp(tStop / UNITS, Actual.cbAlign) * UNITS;
|
||||
|
||||
|
||||
DWORD dwRequest;
|
||||
|
||||
if (!m_bSync) {
|
||||
|
||||
// Break out of the loop either if we get to the end or we're asked
|
||||
// to do something else
|
||||
while (tCurrent < tAlignStop) {
|
||||
|
||||
// Break out without calling EndOfStream if we're asked to
|
||||
// do something different
|
||||
if (CheckRequest(&dwRequest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// queue a first sample
|
||||
if (Actual.cBuffers > 1) {
|
||||
|
||||
hr = QueueSample(tCurrent, tAlignStop, TRUE);
|
||||
bDiscontinuity = FALSE;
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// loop queueing second and waiting for first..
|
||||
while (tCurrent < tAlignStop) {
|
||||
|
||||
hr = QueueSample(tCurrent, tAlignStop, bDiscontinuity);
|
||||
bDiscontinuity = FALSE;
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = CollectAndDeliver(tStart, tStop);
|
||||
if (S_OK != hr) {
|
||||
|
||||
// stop if error, or if downstream filter said
|
||||
// to stop.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Actual.cBuffers > 1) {
|
||||
hr = CollectAndDeliver(tStart, tStop);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// sync version of above loop
|
||||
while (tCurrent < tAlignStop) {
|
||||
|
||||
// Break out without calling EndOfStream if we're asked to
|
||||
// do something different
|
||||
if (CheckRequest(&dwRequest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IMediaSample* pSample;
|
||||
|
||||
hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0);
|
||||
if (FAILED(hr)) {
|
||||
OnError(hr);
|
||||
return;
|
||||
}
|
||||
|
||||
LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS);
|
||||
if (tStopThis > tAlignStop) {
|
||||
tStopThis = tAlignStop;
|
||||
}
|
||||
pSample->SetTime(&tCurrent, &tStopThis);
|
||||
tCurrent = tStopThis;
|
||||
|
||||
if (bDiscontinuity) {
|
||||
pSample->SetDiscontinuity(TRUE);
|
||||
bDiscontinuity = FALSE;
|
||||
}
|
||||
|
||||
hr = m_pReader->SyncReadAligned(pSample);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
pSample->Release();
|
||||
OnError(hr);
|
||||
return;
|
||||
}
|
||||
|
||||
hr = DeliverSample(pSample, tStart, tStop);
|
||||
if (hr != S_OK) {
|
||||
if (FAILED(hr)) {
|
||||
OnError(hr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EndOfStream();
|
||||
}
|
||||
|
||||
// after a flush, cancelled i/o will be waiting for collection
|
||||
// and release
|
||||
void
|
||||
CPullPin::CleanupCancelled(void)
|
||||
{
|
||||
while (1) {
|
||||
IMediaSample * pSample;
|
||||
DWORD_PTR dwUnused;
|
||||
|
||||
HRESULT hr = m_pReader->WaitForNext(
|
||||
0, // no wait
|
||||
&pSample,
|
||||
&dwUnused);
|
||||
if(pSample) {
|
||||
pSample->Release();
|
||||
} else {
|
||||
// no more samples
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: PullPin.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines CPullPin class.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __PULLPIN_H__
|
||||
#define __PULLPIN_H__
|
||||
|
||||
//
|
||||
// CPullPin
|
||||
//
|
||||
// object supporting pulling data from an IAsyncReader interface.
|
||||
// Given a start/stop position, calls a pure Receive method with each
|
||||
// IMediaSample received.
|
||||
//
|
||||
// This is essentially for use in a MemInputPin when it finds itself
|
||||
// connected to an IAsyncReader pin instead of a pushing pin.
|
||||
//
|
||||
|
||||
class CPullPin : public CAMThread
|
||||
{
|
||||
IAsyncReader* m_pReader;
|
||||
REFERENCE_TIME m_tStart;
|
||||
REFERENCE_TIME m_tStop;
|
||||
REFERENCE_TIME m_tDuration;
|
||||
BOOL m_bSync;
|
||||
|
||||
enum ThreadMsg {
|
||||
TM_Pause, // stop pulling and wait for next message
|
||||
TM_Start, // start pulling
|
||||
TM_Exit, // stop and exit
|
||||
};
|
||||
|
||||
ThreadMsg m_State;
|
||||
|
||||
// override pure thread proc from CAMThread
|
||||
DWORD ThreadProc(void);
|
||||
|
||||
// running pull method (check m_bSync)
|
||||
void Process(void);
|
||||
|
||||
// clean up any cancelled i/o after a flush
|
||||
void CleanupCancelled(void);
|
||||
|
||||
// suspend thread from pulling, eg during seek
|
||||
HRESULT PauseThread();
|
||||
|
||||
// start thread pulling - create thread if necy
|
||||
HRESULT StartThread();
|
||||
|
||||
// stop and close thread
|
||||
HRESULT StopThread();
|
||||
|
||||
// called from ProcessAsync to queue and collect requests
|
||||
HRESULT QueueSample(
|
||||
REFERENCE_TIME& tCurrent,
|
||||
REFERENCE_TIME tAlignStop,
|
||||
BOOL bDiscontinuity);
|
||||
|
||||
HRESULT CollectAndDeliver(
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop);
|
||||
|
||||
HRESULT DeliverSample(
|
||||
IMediaSample* pSample,
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop);
|
||||
|
||||
protected:
|
||||
IMemAllocator * m_pAlloc;
|
||||
|
||||
public:
|
||||
CPullPin();
|
||||
virtual ~CPullPin();
|
||||
|
||||
// returns S_OK if successfully connected to an IAsyncReader interface
|
||||
// from this object
|
||||
// Optional allocator should be proposed as a preferred allocator if
|
||||
// necessary
|
||||
// bSync is TRUE if we are to use sync reads instead of the
|
||||
// async methods.
|
||||
HRESULT Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync);
|
||||
|
||||
// disconnect any connection made in Connect
|
||||
HRESULT Disconnect();
|
||||
|
||||
// agree an allocator using RequestAllocator - optional
|
||||
// props param specifies your requirements (non-zero fields).
|
||||
// returns an error code if fail to match requirements.
|
||||
// optional IMemAllocator interface is offered as a preferred allocator
|
||||
// but no error occurs if it can't be met.
|
||||
virtual HRESULT DecideAllocator(
|
||||
IMemAllocator* pAlloc,
|
||||
ALLOCATOR_PROPERTIES * pProps);
|
||||
|
||||
// set start and stop position. if active, will start immediately at
|
||||
// the new position. Default is 0 to duration
|
||||
HRESULT Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop);
|
||||
|
||||
// return the total duration
|
||||
HRESULT Duration(REFERENCE_TIME* ptDuration);
|
||||
|
||||
// start pulling data
|
||||
HRESULT Active(void);
|
||||
|
||||
// stop pulling data
|
||||
HRESULT Inactive(void);
|
||||
|
||||
// helper functions
|
||||
LONGLONG AlignDown(LONGLONG ll, LONG lAlign) {
|
||||
// aligning downwards is just truncation
|
||||
return ll & ~(lAlign-1);
|
||||
};
|
||||
|
||||
LONGLONG AlignUp(LONGLONG ll, LONG lAlign) {
|
||||
// align up: round up to next boundary
|
||||
return (ll + (lAlign -1)) & ~(lAlign -1);
|
||||
};
|
||||
|
||||
// GetReader returns the (addrefed) IAsyncReader interface
|
||||
// for SyncRead etc
|
||||
IAsyncReader* GetReader() {
|
||||
m_pReader->AddRef();
|
||||
return m_pReader;
|
||||
};
|
||||
|
||||
// -- pure --
|
||||
|
||||
// override this to handle data arrival
|
||||
// return value other than S_OK will stop data
|
||||
virtual HRESULT Receive(IMediaSample*) PURE;
|
||||
|
||||
// override this to handle end-of-stream
|
||||
virtual HRESULT EndOfStream(void) PURE;
|
||||
|
||||
// called on runtime errors that will have caused pulling
|
||||
// to stop
|
||||
// these errors are all returned from the upstream filter, who
|
||||
// will have already reported any errors to the filtergraph.
|
||||
virtual void OnError(HRESULT hr) PURE;
|
||||
|
||||
// flush this pin and all downstream
|
||||
virtual HRESULT BeginFlush() PURE;
|
||||
virtual HRESULT EndFlush() PURE;
|
||||
|
||||
};
|
||||
|
||||
#endif //__PULLPIN_H__
|
@ -1,340 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: RefClock.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements the IReferenceClock interface.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#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(TEXT("CBaseReferenceClock::GetTime"));
|
||||
#endif
|
||||
|
||||
if ( !pShed )
|
||||
{
|
||||
DWORD ThreadID;
|
||||
m_hThread = ::CreateThread(NULL, // Security attributes
|
||||
(DWORD) 0, // Initial stack size
|
||||
AdviseThreadFunction, // Thread start address
|
||||
(LPVOID) this, // Thread parameter
|
||||
(DWORD) 0, // Creation flags
|
||||
&ThreadID); // Thread identifier
|
||||
|
||||
if (m_hThread)
|
||||
{
|
||||
SetThreadPriority( m_hThread, THREAD_PRIORITY_TIME_CRITICAL );
|
||||
}
|
||||
else
|
||||
{
|
||||
*phr = E_FAIL;
|
||||
EXECUTE_ASSERT( CloseHandle(m_pSchedule->GetEvent()) );
|
||||
delete m_pSchedule;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBaseReferenceClock::Restart (IN REFERENCE_TIME rtMinTime)
|
||||
{
|
||||
Lock();
|
||||
m_rtLastGotTime = rtMinTime ;
|
||||
Unlock();
|
||||
}
|
||||
|
||||
STDMETHODIMP CBaseReferenceClock::GetTime(REFERENCE_TIME *pTime)
|
||||
{
|
||||
HRESULT hr;
|
||||
if (pTime)
|
||||
{
|
||||
REFERENCE_TIME rtNow;
|
||||
Lock();
|
||||
rtNow = GetPrivateTime();
|
||||
if (rtNow > m_rtLastGotTime)
|
||||
{
|
||||
m_rtLastGotTime = rtNow;
|
||||
hr = S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = S_FALSE;
|
||||
}
|
||||
*pTime = m_rtLastGotTime;
|
||||
Unlock();
|
||||
MSR_INTEGER(m_idGetSystemTime, LONG((*pTime) / (UNITS/MILLISECONDS)) );
|
||||
}
|
||||
else hr = E_POINTER;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Ask for an async notification that a time has elapsed */
|
||||
|
||||
STDMETHODIMP CBaseReferenceClock::AdviseTime(
|
||||
REFERENCE_TIME baseTime, // base reference time
|
||||
REFERENCE_TIME streamTime, // stream offset time
|
||||
HEVENT hEvent, // advise via this event
|
||||
DWORD_PTR *pdwAdviseCookie) // where your cookie goes
|
||||
{
|
||||
CheckPointer(pdwAdviseCookie, E_POINTER);
|
||||
*pdwAdviseCookie = 0;
|
||||
|
||||
// Check that the event is not already set
|
||||
ASSERT(WAIT_TIMEOUT == WaitForSingleObject(HANDLE(hEvent),0));
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
const REFERENCE_TIME lRefTime = baseTime + streamTime;
|
||||
if ( lRefTime <= 0 || lRefTime == MAX_TIME )
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pdwAdviseCookie = m_pSchedule->AddAdvisePacket( lRefTime, 0, HANDLE(hEvent), FALSE );
|
||||
hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
/* Ask for an asynchronous periodic notification that a time has elapsed */
|
||||
|
||||
STDMETHODIMP CBaseReferenceClock::AdvisePeriodic(
|
||||
REFERENCE_TIME StartTime, // starting at this time
|
||||
REFERENCE_TIME PeriodTime, // time between notifications
|
||||
HSEMAPHORE hSemaphore, // advise via a semaphore
|
||||
DWORD_PTR *pdwAdviseCookie) // where your cookie goes
|
||||
{
|
||||
CheckPointer(pdwAdviseCookie, E_POINTER);
|
||||
*pdwAdviseCookie = 0;
|
||||
|
||||
HRESULT hr;
|
||||
if (StartTime > 0 && PeriodTime > 0 && StartTime != MAX_TIME )
|
||||
{
|
||||
*pdwAdviseCookie = m_pSchedule->AddAdvisePacket( StartTime, PeriodTime, HANDLE(hSemaphore), TRUE );
|
||||
hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;
|
||||
}
|
||||
else hr = E_INVALIDARG;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CBaseReferenceClock::Unadvise(DWORD_PTR dwAdviseCookie)
|
||||
{
|
||||
return m_pSchedule->Unadvise(dwAdviseCookie);
|
||||
}
|
||||
|
||||
|
||||
REFERENCE_TIME CBaseReferenceClock::GetPrivateTime()
|
||||
{
|
||||
CAutoLock cObjectLock(this);
|
||||
|
||||
|
||||
/* If the clock has wrapped then the current time will be less than
|
||||
* the last time we were notified so add on the extra milliseconds
|
||||
*
|
||||
* The time period is long enough so that the likelihood of
|
||||
* successive calls spanning the clock cycle is not considered.
|
||||
*/
|
||||
|
||||
DWORD dwTime = timeGetTime();
|
||||
{
|
||||
m_rtPrivateTime += Int32x32To64(UNITS / MILLISECONDS, (DWORD)(dwTime - m_dwPrevSystemTime));
|
||||
m_dwPrevSystemTime = dwTime;
|
||||
}
|
||||
|
||||
return m_rtPrivateTime;
|
||||
}
|
||||
|
||||
|
||||
/* Adjust the current time by the input value. This allows an
|
||||
external time source to work out some of the latency of the clock
|
||||
system and adjust the "current" time accordingly. The intent is
|
||||
that the time returned to the user is synchronised to a clock
|
||||
source and allows drift to be catered for.
|
||||
|
||||
For example: if the clock source detects a drift it can pass a delta
|
||||
to the current time rather than having to set an explicit time.
|
||||
*/
|
||||
|
||||
STDMETHODIMP CBaseReferenceClock::SetTimeDelta(const REFERENCE_TIME & TimeDelta)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
|
||||
// Just break if passed an improper time delta value
|
||||
LONGLONG llDelta = TimeDelta > 0 ? TimeDelta : -TimeDelta;
|
||||
if (llDelta > UNITS * 1000) {
|
||||
DbgLog((LOG_TRACE, 0, TEXT("Bad Time Delta")));
|
||||
//DebugBreak();
|
||||
}
|
||||
|
||||
// We're going to calculate a "severity" for the time change. Max -1
|
||||
// min 8. We'll then use this as the debug logging level for a
|
||||
// debug log message.
|
||||
const LONG usDelta = LONG(TimeDelta/10); // Delta in micro-secs
|
||||
|
||||
DWORD delta = abs(usDelta); // varying delta
|
||||
// Severity == 8 - ceil(log<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;
|
||||
}
|
@ -1,171 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: RefClock.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines the IReferenceClock interface.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __BASEREFCLOCK__
|
||||
#define __BASEREFCLOCK__
|
||||
|
||||
#include "dsschedule.h"
|
||||
|
||||
const UINT RESOLUTION = 1; /* High resolution timer */
|
||||
const INT ADVISE_CACHE = 4; /* Default cache size */
|
||||
const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF; /* Maximum LONGLONG value */
|
||||
|
||||
inline LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME& RT)
|
||||
{
|
||||
/* This converts an arbitrary value representing a reference time
|
||||
into a MILLISECONDS value for use in subsequent system calls */
|
||||
|
||||
return (RT / (UNITS / MILLISECONDS));
|
||||
}
|
||||
|
||||
/* This class hierarchy will support an IReferenceClock interface so
|
||||
that an audio card (or other externally driven clock) can update the
|
||||
system wide clock that everyone uses.
|
||||
|
||||
The interface will be pretty thin with probably just one update method
|
||||
This interface has not yet been defined.
|
||||
*/
|
||||
|
||||
/* This abstract base class implements the IReferenceClock
|
||||
* interface. Classes that actually provide clock signals (from
|
||||
* whatever source) have to be derived from this class.
|
||||
*
|
||||
* The abstract class provides implementations for:
|
||||
* CUnknown support
|
||||
* locking support (CCritSec)
|
||||
* client advise code (creates a thread)
|
||||
*
|
||||
* Question: what can we do about quality? Change the timer
|
||||
* resolution to lower the system load? Up the priority of the
|
||||
* timer thread to force more responsive signals?
|
||||
*
|
||||
* During class construction we create a worker thread that is destroyed during
|
||||
* destuction. This thread executes a series of WaitForSingleObject calls,
|
||||
* waking up when a command is given to the thread or the next wake up point
|
||||
* is reached. The wakeup points are determined by clients making Advise
|
||||
* calls.
|
||||
*
|
||||
* Each advise call defines a point in time when they wish to be notified. A
|
||||
* periodic advise is a series of these such events. We maintain a list of
|
||||
* advise links and calculate when the nearest event notification is due for.
|
||||
* We then call WaitForSingleObject with a timeout equal to this time. The
|
||||
* handle we wait on is used by the class to signal that something has changed
|
||||
* and that we must reschedule the next event. This typically happens when
|
||||
* someone comes in and asks for an advise link while we are waiting for an
|
||||
* event to timeout.
|
||||
*
|
||||
* While we are modifying the list of advise requests we
|
||||
* are protected from interference through a critical section. Clients are NOT
|
||||
* advised through callbacks. One shot clients have an event set, while
|
||||
* periodic clients have a semaphore released for each event notification. A
|
||||
* semaphore allows a client to be kept up to date with the number of events
|
||||
* actually triggered and be assured that they can't miss multiple events being
|
||||
* set.
|
||||
*
|
||||
* Keeping track of advises is taken care of by the CAMSchedule class.
|
||||
*/
|
||||
|
||||
class CBaseReferenceClock
|
||||
: public CUnknown, public IReferenceClock, public CCritSec
|
||||
{
|
||||
protected:
|
||||
virtual ~CBaseReferenceClock(); // Don't let me be created on the stack!
|
||||
public:
|
||||
CBaseReferenceClock(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr, CAMSchedule * pSched = 0 );
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void ** ppv);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
/* IReferenceClock methods */
|
||||
// Derived classes must implement GetPrivateTime(). All our GetTime
|
||||
// does is call GetPrivateTime and then check so that time does not
|
||||
// go backwards. A return code of S_FALSE implies that the internal
|
||||
// clock has gone backwards and GetTime time has halted until internal
|
||||
// time has caught up. (Don't know if this will be much use to folk,
|
||||
// but it seems odd not to use the return code for something useful.)
|
||||
STDMETHODIMP GetTime(REFERENCE_TIME *pTime);
|
||||
// When this is called, it sets m_rtLastGotTime to the time it returns.
|
||||
|
||||
/* Provide standard mechanisms for scheduling events */
|
||||
|
||||
/* Ask for an async notification that a time has elapsed */
|
||||
STDMETHODIMP AdviseTime(
|
||||
REFERENCE_TIME baseTime, // base reference time
|
||||
REFERENCE_TIME streamTime, // stream offset time
|
||||
HEVENT hEvent, // advise via this event
|
||||
DWORD_PTR *pdwAdviseCookie // where your cookie goes
|
||||
);
|
||||
|
||||
/* Ask for an asynchronous periodic notification that a time has elapsed */
|
||||
STDMETHODIMP AdvisePeriodic(
|
||||
REFERENCE_TIME StartTime, // starting at this time
|
||||
REFERENCE_TIME PeriodTime, // time between notifications
|
||||
HSEMAPHORE hSemaphore, // advise via a semaphore
|
||||
DWORD_PTR *pdwAdviseCookie // where your cookie goes
|
||||
);
|
||||
|
||||
/* Cancel a request for notification(s) - if the notification was
|
||||
* a one shot timer then this function doesn't need to be called
|
||||
* as the advise is automatically cancelled, however it does no
|
||||
* harm to explicitly cancel a one-shot advise. It is REQUIRED that
|
||||
* clients call Unadvise to clear a Periodic advise setting.
|
||||
*/
|
||||
|
||||
STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie);
|
||||
|
||||
/* Methods for the benefit of derived classes or outer objects */
|
||||
|
||||
// GetPrivateTime() is the REAL clock. GetTime is just a cover for
|
||||
// it. Derived classes will probably override this method but not
|
||||
// GetTime() itself.
|
||||
// The important point about GetPrivateTime() is it's allowed to go
|
||||
// backwards. Our GetTime() will keep returning the LastGotTime
|
||||
// until GetPrivateTime() catches up.
|
||||
virtual REFERENCE_TIME GetPrivateTime();
|
||||
|
||||
/* Provide a method for correcting drift */
|
||||
STDMETHODIMP SetTimeDelta( const REFERENCE_TIME& TimeDelta );
|
||||
|
||||
CAMSchedule * GetSchedule() const { return m_pSchedule; }
|
||||
|
||||
private:
|
||||
REFERENCE_TIME m_rtPrivateTime; // Current best estimate of time
|
||||
DWORD m_dwPrevSystemTime; // Last vaule we got from timeGetTime
|
||||
REFERENCE_TIME m_rtLastGotTime; // Last time returned by GetTime
|
||||
REFERENCE_TIME m_rtNextAdvise; // Time of next advise
|
||||
UINT m_TimerResolution;
|
||||
|
||||
#ifdef PERF
|
||||
int m_idGetSystemTime;
|
||||
#endif
|
||||
|
||||
// Thread stuff
|
||||
public:
|
||||
void TriggerThread() // Wakes thread up. Need to do this if
|
||||
{ // time to next advise needs reevaluating.
|
||||
EXECUTE_ASSERT(SetEvent(m_pSchedule->GetEvent()));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
BOOL m_bAbort; // Flag used for thread shutdown
|
||||
HANDLE m_hThread; // Thread handle
|
||||
|
||||
HRESULT AdviseThread(); // Method in which the advise thread runs
|
||||
static DWORD __stdcall AdviseThreadFunction(LPVOID); // Function used to get there
|
||||
|
||||
protected:
|
||||
CAMSchedule * const m_pSchedule;
|
||||
|
||||
void Restart (IN REFERENCE_TIME rtMinTime = 0I64) ;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,116 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: RefTime.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines CRefTime, a class that manages
|
||||
// reference times.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// CRefTime
|
||||
//
|
||||
// Manage reference times.
|
||||
// Shares same data layout as REFERENCE_TIME, but adds some (nonvirtual)
|
||||
// functions providing simple comparison, conversion and arithmetic.
|
||||
//
|
||||
// A reference time (at the moment) is a unit of seconds represented in
|
||||
// 100ns units as is used in the Win32 FILETIME structure. BUT the time
|
||||
// a REFERENCE_TIME represents is NOT the time elapsed since 1/1/1601 it
|
||||
// will either be stream time or reference time depending upon context
|
||||
//
|
||||
// This class provides simple arithmetic operations on reference times
|
||||
//
|
||||
// keep non-virtual otherwise the data layout will not be the same as
|
||||
// REFERENCE_TIME
|
||||
|
||||
|
||||
// -----
|
||||
// note that you are safe to cast a CRefTime* to a REFERENCE_TIME*, but
|
||||
// you will need to do so explicitly
|
||||
// -----
|
||||
|
||||
|
||||
#ifndef __REFTIME__
|
||||
#define __REFTIME__
|
||||
|
||||
|
||||
const LONGLONG MILLISECONDS = (1000); // 10 ^ 3
|
||||
const LONGLONG NANOSECONDS = (1000000000); // 10 ^ 9
|
||||
const LONGLONG UNITS = (NANOSECONDS / 100); // 10 ^ 7
|
||||
|
||||
/* Unfortunately an inline function here generates a call to __allmul
|
||||
- even for constants!
|
||||
*/
|
||||
#define MILLISECONDS_TO_100NS_UNITS(lMs) \
|
||||
Int32x32To64((lMs), (UNITS / MILLISECONDS))
|
||||
|
||||
class CRefTime
|
||||
{
|
||||
public:
|
||||
|
||||
// *MUST* be the only data member so that this class is exactly
|
||||
// equivalent to a REFERENCE_TIME.
|
||||
// Also, must be *no virtual functions*
|
||||
|
||||
REFERENCE_TIME m_time;
|
||||
|
||||
inline CRefTime()
|
||||
{
|
||||
// default to 0 time
|
||||
m_time = 0;
|
||||
};
|
||||
|
||||
inline CRefTime(LONG msecs)
|
||||
{
|
||||
m_time = MILLISECONDS_TO_100NS_UNITS(msecs);
|
||||
};
|
||||
|
||||
inline CRefTime(REFERENCE_TIME rt)
|
||||
{
|
||||
m_time = rt;
|
||||
};
|
||||
|
||||
inline operator REFERENCE_TIME() const
|
||||
{
|
||||
return m_time;
|
||||
};
|
||||
|
||||
inline CRefTime& operator=(const CRefTime& rt)
|
||||
{
|
||||
m_time = rt.m_time;
|
||||
return *this;
|
||||
};
|
||||
|
||||
inline CRefTime& operator=(const LONGLONG ll)
|
||||
{
|
||||
m_time = ll;
|
||||
return *this;
|
||||
};
|
||||
|
||||
inline CRefTime& operator+=(const CRefTime& rt)
|
||||
{
|
||||
return (*this = *this + rt);
|
||||
};
|
||||
|
||||
inline CRefTime& operator-=(const CRefTime& rt)
|
||||
{
|
||||
return (*this = *this - rt);
|
||||
};
|
||||
|
||||
inline LONG Millisecs(void)
|
||||
{
|
||||
return (LONG)(m_time / (UNITS / MILLISECONDS));
|
||||
};
|
||||
|
||||
inline LONGLONG GetUnits(void)
|
||||
{
|
||||
return m_time;
|
||||
};
|
||||
};
|
||||
|
||||
const LONGLONG TimeZero = 0;
|
||||
|
||||
#endif /* __REFTIME__ */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,478 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: RenBase.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a generic ActiveX base renderer
|
||||
// class.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __RENBASE__
|
||||
#define __RENBASE__
|
||||
|
||||
// Forward class declarations
|
||||
|
||||
class CBaseRenderer;
|
||||
class CBaseVideoRenderer;
|
||||
class CRendererInputPin;
|
||||
|
||||
// This is our input pin class that channels calls to the renderer
|
||||
|
||||
class CRendererInputPin : public CBaseInputPin
|
||||
{
|
||||
protected:
|
||||
|
||||
CBaseRenderer *m_pRenderer;
|
||||
|
||||
public:
|
||||
|
||||
CRendererInputPin(CBaseRenderer *pRenderer,
|
||||
HRESULT *phr,
|
||||
LPCWSTR Name);
|
||||
|
||||
// Overriden from the base pin classes
|
||||
|
||||
HRESULT BreakConnect();
|
||||
HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
HRESULT SetMediaType(const CMediaType *pmt);
|
||||
HRESULT CheckMediaType(const CMediaType *pmt);
|
||||
HRESULT Active();
|
||||
HRESULT Inactive();
|
||||
|
||||
// Add rendering behaviour to interface functions
|
||||
|
||||
STDMETHODIMP QueryId(LPWSTR *Id);
|
||||
STDMETHODIMP EndOfStream();
|
||||
STDMETHODIMP BeginFlush();
|
||||
STDMETHODIMP EndFlush();
|
||||
STDMETHODIMP Receive(IMediaSample *pMediaSample);
|
||||
|
||||
// Helper
|
||||
IMemAllocator inline *Allocator() const
|
||||
{
|
||||
return m_pAllocator;
|
||||
}
|
||||
};
|
||||
|
||||
// Main renderer class that handles synchronisation and state changes
|
||||
|
||||
class CBaseRenderer : public CBaseFilter
|
||||
{
|
||||
protected:
|
||||
|
||||
friend class CRendererInputPin;
|
||||
|
||||
friend void CALLBACK EndOfStreamTimer(UINT uID, // Timer identifier
|
||||
UINT uMsg, // Not currently used
|
||||
DWORD_PTR dwUser, // User information
|
||||
DWORD_PTR dw1, // Windows reserved
|
||||
DWORD_PTR dw2); // Is also reserved
|
||||
|
||||
CRendererPosPassThru *m_pPosition; // Media seeking pass by object
|
||||
CAMEvent m_RenderEvent; // Used to signal timer events
|
||||
CAMEvent m_ThreadSignal; // Signalled to release worker thread
|
||||
CAMEvent m_evComplete; // Signalled when state complete
|
||||
BOOL m_bAbort; // Stop us from rendering more data
|
||||
BOOL m_bStreaming; // Are we currently streaming
|
||||
DWORD_PTR m_dwAdvise; // Timer advise cookie
|
||||
IMediaSample *m_pMediaSample; // Current image media sample
|
||||
BOOL m_bEOS; // Any more samples in the stream
|
||||
BOOL m_bEOSDelivered; // Have we delivered an EC_COMPLETE
|
||||
CRendererInputPin *m_pInputPin; // Our renderer input pin object
|
||||
CCritSec m_InterfaceLock; // Critical section for interfaces
|
||||
CCritSec m_RendererLock; // Controls access to internals
|
||||
IQualityControl * m_pQSink; // QualityControl sink
|
||||
BOOL m_bRepaintStatus; // Can we signal an EC_REPAINT
|
||||
// Avoid some deadlocks by tracking filter during stop
|
||||
volatile BOOL m_bInReceive; // Inside Receive between PrepareReceive
|
||||
// And actually processing the sample
|
||||
REFERENCE_TIME m_SignalTime; // Time when we signal EC_COMPLETE
|
||||
UINT m_EndOfStreamTimer; // Used to signal end of stream
|
||||
CCritSec m_ObjectCreationLock; // This lock protects the creation and
|
||||
// of m_pPosition and m_pInputPin. It
|
||||
// ensures that two threads cannot create
|
||||
// either object simultaneously.
|
||||
|
||||
public:
|
||||
|
||||
CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer
|
||||
TCHAR *pName, // Debug ONLY description
|
||||
LPUNKNOWN pUnk, // Aggregated owner object
|
||||
HRESULT *phr); // General OLE return code
|
||||
|
||||
~CBaseRenderer();
|
||||
|
||||
// Overriden to say what interfaces we support and where
|
||||
|
||||
virtual HRESULT GetMediaPositionInterface(REFIID riid,void **ppv);
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID, void **);
|
||||
|
||||
virtual HRESULT SourceThreadCanWait(BOOL bCanWait);
|
||||
|
||||
#ifdef DEBUG
|
||||
// Debug only dump of the renderer state
|
||||
void DisplayRendererState();
|
||||
#endif
|
||||
virtual HRESULT WaitForRenderTime();
|
||||
virtual HRESULT CompleteStateChange(FILTER_STATE OldState);
|
||||
|
||||
// Return internal information about this filter
|
||||
|
||||
BOOL IsEndOfStream() { return m_bEOS; };
|
||||
BOOL IsEndOfStreamDelivered() { return m_bEOSDelivered; };
|
||||
BOOL IsStreaming() { return m_bStreaming; };
|
||||
void SetAbortSignal(BOOL bAbort) { m_bAbort = bAbort; };
|
||||
virtual void OnReceiveFirstSample(IMediaSample *pMediaSample) { };
|
||||
CAMEvent *GetRenderEvent() { return &m_RenderEvent; };
|
||||
|
||||
// Permit access to the transition state
|
||||
|
||||
void Ready() { m_evComplete.Set(); };
|
||||
void NotReady() { m_evComplete.Reset(); };
|
||||
BOOL CheckReady() { return m_evComplete.Check(); };
|
||||
|
||||
virtual int GetPinCount();
|
||||
virtual CBasePin *GetPin(int n);
|
||||
FILTER_STATE GetRealState();
|
||||
void SendRepaint();
|
||||
void SendNotifyWindow(IPin *pPin,HWND hwnd);
|
||||
BOOL OnDisplayChange();
|
||||
void SetRepaintStatus(BOOL bRepaint);
|
||||
|
||||
// Override the filter and pin interface functions
|
||||
|
||||
STDMETHODIMP Stop();
|
||||
STDMETHODIMP Pause();
|
||||
STDMETHODIMP Run(REFERENCE_TIME StartTime);
|
||||
STDMETHODIMP GetState(DWORD dwMSecs,FILTER_STATE *State);
|
||||
STDMETHODIMP FindPin(LPCWSTR Id, IPin **ppPin);
|
||||
|
||||
// These are available for a quality management implementation
|
||||
|
||||
virtual void OnRenderStart(IMediaSample *pMediaSample);
|
||||
virtual void OnRenderEnd(IMediaSample *pMediaSample);
|
||||
virtual HRESULT OnStartStreaming() { return NOERROR; };
|
||||
virtual HRESULT OnStopStreaming() { return NOERROR; };
|
||||
virtual void OnWaitStart() { };
|
||||
virtual void OnWaitEnd() { };
|
||||
virtual void PrepareRender() { };
|
||||
|
||||
#ifdef PERF
|
||||
REFERENCE_TIME m_trRenderStart; // Just before we started drawing
|
||||
// Set in OnRenderStart, Used in OnRenderEnd
|
||||
int m_idBaseStamp; // MSR_id for frame time stamp
|
||||
int m_idBaseRenderTime; // MSR_id for true wait time
|
||||
int m_idBaseAccuracy; // MSR_id for time frame is late (int)
|
||||
#endif
|
||||
|
||||
// Quality management implementation for scheduling rendering
|
||||
|
||||
virtual BOOL ScheduleSample(IMediaSample *pMediaSample);
|
||||
virtual HRESULT GetSampleTimes(IMediaSample *pMediaSample,
|
||||
REFERENCE_TIME *pStartTime,
|
||||
REFERENCE_TIME *pEndTime);
|
||||
|
||||
virtual HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,
|
||||
REFERENCE_TIME *ptrStart,
|
||||
REFERENCE_TIME *ptrEnd);
|
||||
|
||||
// Lots of end of stream complexities
|
||||
|
||||
void TimerCallback();
|
||||
void ResetEndOfStreamTimer();
|
||||
HRESULT NotifyEndOfStream();
|
||||
virtual HRESULT SendEndOfStream();
|
||||
virtual HRESULT ResetEndOfStream();
|
||||
virtual HRESULT EndOfStream();
|
||||
|
||||
// Rendering is based around the clock
|
||||
|
||||
void SignalTimerFired();
|
||||
virtual HRESULT CancelNotification();
|
||||
virtual HRESULT ClearPendingSample();
|
||||
|
||||
// Called when the filter changes state
|
||||
|
||||
virtual HRESULT Active();
|
||||
virtual HRESULT Inactive();
|
||||
virtual HRESULT StartStreaming();
|
||||
virtual HRESULT StopStreaming();
|
||||
virtual HRESULT BeginFlush();
|
||||
virtual HRESULT EndFlush();
|
||||
|
||||
// Deal with connections and type changes
|
||||
|
||||
virtual HRESULT BreakConnect();
|
||||
virtual HRESULT SetMediaType(const CMediaType *pmt);
|
||||
virtual HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
|
||||
// These look after the handling of data samples
|
||||
|
||||
virtual HRESULT PrepareReceive(IMediaSample *pMediaSample);
|
||||
virtual HRESULT Receive(IMediaSample *pMediaSample);
|
||||
virtual BOOL HaveCurrentSample();
|
||||
virtual IMediaSample *GetCurrentSample();
|
||||
virtual HRESULT Render(IMediaSample *pMediaSample);
|
||||
|
||||
// Derived classes MUST override these
|
||||
virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE;
|
||||
virtual HRESULT CheckMediaType(const CMediaType *) PURE;
|
||||
|
||||
// Helper
|
||||
void WaitForReceiveToComplete();
|
||||
};
|
||||
|
||||
|
||||
// CBaseVideoRenderer is a renderer class (see its ancestor class) and
|
||||
// it handles scheduling of media samples so that they are drawn at the
|
||||
// correct time by the reference clock. It implements a degradation
|
||||
// strategy. Possible degradation modes are:
|
||||
// Drop frames here (only useful if the drawing takes significant time)
|
||||
// Signal supplier (upstream) to drop some frame(s) - i.e. one-off skip.
|
||||
// Signal supplier to change the frame rate - i.e. ongoing skipping.
|
||||
// Or any combination of the above.
|
||||
// In order to determine what's useful to try we need to know what's going
|
||||
// on. This is done by timing various operations (including the supplier).
|
||||
// This timing is done by using timeGetTime as it is accurate enough and
|
||||
// usually cheaper than calling the reference clock. It also tells the
|
||||
// truth if there is an audio break and the reference clock stops.
|
||||
// We provide a number of public entry points (named OnXxxStart, OnXxxEnd)
|
||||
// which the rest of the renderer calls at significant moments. These do
|
||||
// the timing.
|
||||
|
||||
// the number of frames that the sliding averages are averaged over.
|
||||
// the rule is (1024*NewObservation + (AVGPERIOD-1) * PreviousAverage)/AVGPERIOD
|
||||
#define AVGPERIOD 4
|
||||
#define DO_MOVING_AVG(avg,obs) (avg = (1024*obs + (AVGPERIOD-1)*avg)/AVGPERIOD)
|
||||
// Spot the bug in this macro - I can't. but it doesn't work!
|
||||
|
||||
class CBaseVideoRenderer : public CBaseRenderer, // Base renderer class
|
||||
public IQualProp, // Property page guff
|
||||
public IQualityControl // Allow throttling
|
||||
{
|
||||
protected:
|
||||
|
||||
// Hungarian:
|
||||
// tFoo is the time Foo in mSec (beware m_tStart from filter.h)
|
||||
// trBar is the time Bar by the reference clock
|
||||
|
||||
//******************************************************************
|
||||
// State variables to control synchronisation
|
||||
//******************************************************************
|
||||
|
||||
// Control of sending Quality messages. We need to know whether
|
||||
// we are in trouble (e.g. frames being dropped) and where the time
|
||||
// is being spent.
|
||||
|
||||
// When we drop a frame we play the next one early.
|
||||
// The frame after that is likely to wait before drawing and counting this
|
||||
// wait as spare time is unfair, so we count it as a zero wait.
|
||||
// We therefore need to know whether we are playing frames early or not.
|
||||
|
||||
int m_nNormal; // The number of consecutive frames
|
||||
// drawn at their normal time (not early)
|
||||
// -1 means we just dropped a frame.
|
||||
|
||||
#ifdef PERF
|
||||
BOOL m_bDrawLateFrames; // Don't drop any frames (debug and I'm
|
||||
// not keen on people using it!)
|
||||
#endif
|
||||
|
||||
BOOL m_bSupplierHandlingQuality;// The response to Quality messages says
|
||||
// our supplier is handling things.
|
||||
// We will allow things to go extra late
|
||||
// before dropping frames. We will play
|
||||
// very early after he has dropped one.
|
||||
|
||||
// Control of scheduling, frame dropping etc.
|
||||
// We need to know where the time is being spent so as to tell whether
|
||||
// we should be taking action here, signalling supplier or what.
|
||||
// The variables are initialised to a mode of NOT dropping frames.
|
||||
// They will tell the truth after a few frames.
|
||||
// We typically record a start time for an event, later we get the time
|
||||
// again and subtract to get the elapsed time, and we average this over
|
||||
// a few frames. The average is used to tell what mode we are in.
|
||||
|
||||
// Although these are reference times (64 bit) they are all DIFFERENCES
|
||||
// between times which are small. An int will go up to 214 secs before
|
||||
// overflow. Avoiding 64 bit multiplications and divisions seems
|
||||
// worth while.
|
||||
|
||||
|
||||
|
||||
// Audio-video throttling. If the user has turned up audio quality
|
||||
// very high (in principle it could be any other stream, not just audio)
|
||||
// then we can receive cries for help via the graph manager. In this case
|
||||
// we put in a wait for some time after rendering each frame.
|
||||
int m_trThrottle;
|
||||
|
||||
// The time taken to render (i.e. BitBlt) frames controls which component
|
||||
// needs to degrade. If the blt is expensive, the renderer degrades.
|
||||
// If the blt is cheap it's done anyway and the supplier degrades.
|
||||
int m_trRenderAvg; // Time frames are taking to blt
|
||||
int m_trRenderLast; // Time for last frame blt
|
||||
int m_tRenderStart; // Just before we started drawing (mSec)
|
||||
// derived from timeGetTime.
|
||||
|
||||
// When frames are dropped we will play the next frame as early as we can.
|
||||
// If it was a false alarm and the machine is fast we slide gently back to
|
||||
// normal timing. To do this, we record the offset showing just how early
|
||||
// we really are. This will normally be negative meaning early or zero.
|
||||
int m_trEarliness;
|
||||
|
||||
// Target provides slow long-term feedback to try to reduce the
|
||||
// average sync offset to zero. Whenever a frame is actually rendered
|
||||
// early we add a msec or two, whenever late we take off a few.
|
||||
// We add or take off 1/32 of the error time.
|
||||
// Eventually we should be hovering around zero. For a really bad case
|
||||
// where we were (say) 300mSec off, it might take 100 odd frames to
|
||||
// settle down. The rate of change of this is intended to be slower
|
||||
// than any other mechanism in Quartz, thereby avoiding hunting.
|
||||
int m_trTarget;
|
||||
|
||||
// The proportion of time spent waiting for the right moment to blt
|
||||
// controls whether we bother to drop a frame or whether we reckon that
|
||||
// we're doing well enough that we can stand a one-frame glitch.
|
||||
int m_trWaitAvg; // Average of last few wait times
|
||||
// (actually we just average how early
|
||||
// we were). Negative here means LATE.
|
||||
|
||||
// The average inter-frame time.
|
||||
// This is used to calculate the proportion of the time used by the
|
||||
// three operations (supplying us, waiting, rendering)
|
||||
int m_trFrameAvg; // Average inter-frame time
|
||||
int m_trDuration; // duration of last frame.
|
||||
|
||||
#ifdef PERF
|
||||
// Performance logging identifiers
|
||||
int m_idTimeStamp; // MSR_id for frame time stamp
|
||||
int m_idEarliness; // MSR_id for earliness fudge
|
||||
int m_idTarget; // MSR_id for Target fudge
|
||||
int m_idWaitReal; // MSR_id for true wait time
|
||||
int m_idWait; // MSR_id for wait time recorded
|
||||
int m_idFrameAccuracy; // MSR_id for time frame is late (int)
|
||||
int m_idRenderAvg; // MSR_id for Render time recorded (int)
|
||||
int m_idSchLateTime; // MSR_id for lateness at scheduler
|
||||
int m_idQualityRate; // MSR_id for Quality rate requested
|
||||
int m_idQualityTime; // MSR_id for Quality time requested
|
||||
int m_idDecision; // MSR_id for decision code
|
||||
int m_idDuration; // MSR_id for duration of a frame
|
||||
int m_idThrottle; // MSR_id for audio-video throttling
|
||||
//int m_idDebug; // MSR_id for trace style debugging
|
||||
//int m_idSendQuality; // MSR_id for timing the notifications per se
|
||||
#endif // PERF
|
||||
REFERENCE_TIME m_trRememberStampForPerf; // original time stamp of frame
|
||||
// with no earliness fudges etc.
|
||||
#ifdef PERF
|
||||
REFERENCE_TIME m_trRememberFrameForPerf; // time when previous frame rendered
|
||||
|
||||
// debug...
|
||||
int m_idFrameAvg;
|
||||
int m_idWaitAvg;
|
||||
#endif
|
||||
|
||||
// PROPERTY PAGE
|
||||
// This has edit fields that show the user what's happening
|
||||
// These member variables hold these counts.
|
||||
|
||||
int m_cFramesDropped; // cumulative frames dropped IN THE RENDERER
|
||||
int m_cFramesDrawn; // Frames since streaming started seen BY THE
|
||||
// RENDERER (some may be dropped upstream)
|
||||
|
||||
// Next two support average sync offset and standard deviation of sync offset.
|
||||
LONGLONG m_iTotAcc; // Sum of accuracies in mSec
|
||||
LONGLONG m_iSumSqAcc; // Sum of squares of (accuracies in mSec)
|
||||
|
||||
// Next two allow jitter calculation. Jitter is std deviation of frame time.
|
||||
REFERENCE_TIME m_trLastDraw; // Time of prev frame (for inter-frame times)
|
||||
LONGLONG m_iSumSqFrameTime; // Sum of squares of (inter-frame time in mSec)
|
||||
LONGLONG m_iSumFrameTime; // Sum of inter-frame times in mSec
|
||||
|
||||
// To get performance statistics on frame rate, jitter etc, we need
|
||||
// to record the lateness and inter-frame time. What we actually need are the
|
||||
// data above (sum, sum of squares and number of entries for each) but the data
|
||||
// is generated just ahead of time and only later do we discover whether the
|
||||
// frame was actually drawn or not. So we have to hang on to the data
|
||||
int m_trLate; // hold onto frame lateness
|
||||
int m_trFrame; // hold onto inter-frame time
|
||||
|
||||
int m_tStreamingStart; // if streaming then time streaming started
|
||||
// else time of last streaming session
|
||||
// used for property page statistics
|
||||
#ifdef PERF
|
||||
LONGLONG m_llTimeOffset; // timeGetTime()*10000+m_llTimeOffset==ref time
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
|
||||
CBaseVideoRenderer(REFCLSID RenderClass, // CLSID for this renderer
|
||||
TCHAR *pName, // Debug ONLY description
|
||||
LPUNKNOWN pUnk, // Aggregated owner object
|
||||
HRESULT *phr); // General OLE return code
|
||||
|
||||
~CBaseVideoRenderer();
|
||||
|
||||
// IQualityControl methods - Notify allows audio-video throttling
|
||||
|
||||
STDMETHODIMP SetSink( IQualityControl * piqc);
|
||||
STDMETHODIMP Notify( IBaseFilter * pSelf, Quality q);
|
||||
|
||||
// These provide a full video quality management implementation
|
||||
|
||||
void OnRenderStart(IMediaSample *pMediaSample);
|
||||
void OnRenderEnd(IMediaSample *pMediaSample);
|
||||
void OnWaitStart();
|
||||
void OnWaitEnd();
|
||||
HRESULT OnStartStreaming();
|
||||
HRESULT OnStopStreaming();
|
||||
void ThrottleWait();
|
||||
|
||||
// Handle the statistics gathering for our quality management
|
||||
|
||||
void PreparePerformanceData(int trLate, int trFrame);
|
||||
virtual void RecordFrameLateness(int trLate, int trFrame);
|
||||
virtual void OnDirectRender(IMediaSample *pMediaSample);
|
||||
virtual HRESULT ResetStreamingTimes();
|
||||
BOOL ScheduleSample(IMediaSample *pMediaSample);
|
||||
HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,
|
||||
REFERENCE_TIME *ptrStart,
|
||||
REFERENCE_TIME *ptrEnd);
|
||||
|
||||
virtual HRESULT SendQuality(REFERENCE_TIME trLate, REFERENCE_TIME trRealStream);
|
||||
STDMETHODIMP JoinFilterGraph(IFilterGraph * pGraph, LPCWSTR pName);
|
||||
|
||||
//
|
||||
// Do estimates for standard deviations for per-frame
|
||||
// statistics
|
||||
//
|
||||
// *piResult = (llSumSq - iTot * iTot / m_cFramesDrawn - 1) /
|
||||
// (m_cFramesDrawn - 2)
|
||||
// or 0 if m_cFramesDrawn <= 3
|
||||
//
|
||||
HRESULT GetStdDev(
|
||||
int nSamples,
|
||||
int *piResult,
|
||||
LONGLONG llSumSq,
|
||||
LONGLONG iTot
|
||||
);
|
||||
public:
|
||||
|
||||
// IQualProp property page support
|
||||
|
||||
STDMETHODIMP get_FramesDroppedInRenderer(int *cFramesDropped);
|
||||
STDMETHODIMP get_FramesDrawn(int *pcFramesDrawn);
|
||||
STDMETHODIMP get_AvgFrameRate(int *piAvgFrameRate);
|
||||
STDMETHODIMP get_Jitter(int *piJitter);
|
||||
STDMETHODIMP get_AvgSyncOffset(int *piAvg);
|
||||
STDMETHODIMP get_DevSyncOffset(int *piDev);
|
||||
|
||||
// Implement an IUnknown interface and expose IQualProp
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,VOID **ppv);
|
||||
};
|
||||
|
||||
#endif // __RENBASE__
|
||||
|
@ -1,287 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Schedule.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#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_PTR CAMSchedule::AddAdvisePacket
|
||||
( const REFERENCE_TIME & time1
|
||||
, const REFERENCE_TIME & time2
|
||||
, HANDLE h, BOOL periodic
|
||||
)
|
||||
{
|
||||
// Since we use MAX_TIME as a sentry, we can't afford to
|
||||
// schedule a notification at MAX_TIME
|
||||
ASSERT( time1 < MAX_TIME );
|
||||
DWORD_PTR Result;
|
||||
CAdvisePacket * p;
|
||||
|
||||
m_Serialize.Lock();
|
||||
|
||||
if (m_pAdviseCache)
|
||||
{
|
||||
p = m_pAdviseCache;
|
||||
m_pAdviseCache = p->m_next;
|
||||
--m_dwCacheCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = new CAdvisePacket();
|
||||
}
|
||||
if (p)
|
||||
{
|
||||
p->m_rtEventTime = time1; p->m_rtPeriod = time2;
|
||||
p->m_hNotify = h; p->m_bPeriodic = periodic;
|
||||
Result = AddAdvisePacket( p );
|
||||
}
|
||||
else Result = 0;
|
||||
|
||||
m_Serialize.Unlock();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
HRESULT CAMSchedule::Unadvise(DWORD_PTR dwAdviseCookie)
|
||||
{
|
||||
HRESULT hr = S_FALSE;
|
||||
CAdvisePacket * p_prev = &head;
|
||||
CAdvisePacket * p_n;
|
||||
m_Serialize.Lock();
|
||||
while (1) // The Next() method returns NULL when it hits z
|
||||
{
|
||||
p_n = p_prev->Next();
|
||||
if (!p_n) break;
|
||||
|
||||
if ( p_n->m_dwAdviseCookie == dwAdviseCookie )
|
||||
{
|
||||
Delete( p_prev->RemoveNext() );
|
||||
--m_dwAdviseCount;
|
||||
hr = S_OK;
|
||||
// Having found one cookie that matches, there should be no more
|
||||
#ifdef DEBUG
|
||||
while (p_n = p_prev->Next())
|
||||
{
|
||||
ASSERT(p_n->m_dwAdviseCookie != dwAdviseCookie);
|
||||
p_prev = p_n;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
p_prev = p_n;
|
||||
};
|
||||
m_Serialize.Unlock();
|
||||
return hr;
|
||||
}
|
||||
|
||||
REFERENCE_TIME CAMSchedule::Advise( const REFERENCE_TIME & rtTime )
|
||||
{
|
||||
REFERENCE_TIME rtNextTime;
|
||||
CAdvisePacket * pAdvise;
|
||||
|
||||
DbgLog((LOG_TIMING, 2,
|
||||
TEXT("CAMSchedule::Advise( %lu ms )"), ULONG(rtTime / (UNITS / MILLISECONDS))));
|
||||
|
||||
CAutoLock lck(&m_Serialize);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (DbgCheckModuleLevel(LOG_TIMING, 4)) DumpLinkedList();
|
||||
#endif
|
||||
|
||||
// Note - DON'T cache the difference, it might overflow
|
||||
while ( rtTime >= (rtNextTime = (pAdvise=head.m_next)->m_rtEventTime) &&
|
||||
!pAdvise->IsZ() )
|
||||
{
|
||||
ASSERT(pAdvise->m_dwAdviseCookie); // If this is zero, its the head or the tail!!
|
||||
|
||||
ASSERT(pAdvise->m_hNotify != INVALID_HANDLE_VALUE);
|
||||
|
||||
if (pAdvise->m_bPeriodic == TRUE)
|
||||
{
|
||||
ReleaseSemaphore(pAdvise->m_hNotify,1,NULL);
|
||||
pAdvise->m_rtEventTime += pAdvise->m_rtPeriod;
|
||||
ShuntHead();
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT( pAdvise->m_bPeriodic == FALSE );
|
||||
EXECUTE_ASSERT(SetEvent(pAdvise->m_hNotify));
|
||||
--m_dwAdviseCount;
|
||||
Delete( head.RemoveNext() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DbgLog((LOG_TIMING, 3,
|
||||
TEXT("CAMSchedule::Advise() Next time stamp: %lu ms, for advise %lu."),
|
||||
DWORD(rtNextTime / (UNITS / MILLISECONDS)), pAdvise->m_dwAdviseCookie ));
|
||||
|
||||
return rtNextTime;
|
||||
}
|
||||
|
||||
/* Private methods */
|
||||
|
||||
DWORD_PTR CAMSchedule::AddAdvisePacket( CAdvisePacket * pPacket )
|
||||
{
|
||||
ASSERT(pPacket->m_rtEventTime >= 0 && pPacket->m_rtEventTime < MAX_TIME);
|
||||
ASSERT(CritCheckIn(&m_Serialize));
|
||||
|
||||
CAdvisePacket * p_prev = &head;
|
||||
CAdvisePacket * p_n;
|
||||
|
||||
const DWORD_PTR Result = pPacket->m_dwAdviseCookie = ++m_dwNextCookie;
|
||||
// This relies on the fact that z is a sentry with a maximal m_rtEventTime
|
||||
for(;;p_prev = p_n)
|
||||
{
|
||||
p_n = p_prev->m_next;
|
||||
if ( p_n->m_rtEventTime >= pPacket->m_rtEventTime ) break;
|
||||
}
|
||||
p_prev->InsertAfter( pPacket );
|
||||
++m_dwAdviseCount;
|
||||
|
||||
DbgLog((LOG_TIMING, 2, TEXT("Added advise %lu, for thread 0x%02X, scheduled at %lu"),
|
||||
pPacket->m_dwAdviseCookie, GetCurrentThreadId(), (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) ));
|
||||
|
||||
// If packet added at the head, then clock needs to re-evaluate wait time.
|
||||
if ( p_prev == &head ) SetEvent( m_ev );
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
void CAMSchedule::Delete( CAdvisePacket * pPacket )
|
||||
{
|
||||
if ( m_dwCacheCount >= dwCacheMax ) delete pPacket;
|
||||
else
|
||||
{
|
||||
m_Serialize.Lock();
|
||||
pPacket->m_next = m_pAdviseCache;
|
||||
m_pAdviseCache = pPacket;
|
||||
++m_dwCacheCount;
|
||||
m_Serialize.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Takes the head of the list & repositions it
|
||||
void CAMSchedule::ShuntHead()
|
||||
{
|
||||
CAdvisePacket * p_prev = &head;
|
||||
CAdvisePacket * p_n;
|
||||
|
||||
m_Serialize.Lock();
|
||||
CAdvisePacket *const pPacket = head.m_next;
|
||||
|
||||
// This will catch both an empty list,
|
||||
// and if somehow a MAX_TIME time gets into the list
|
||||
// (which would also break this method).
|
||||
ASSERT( pPacket->m_rtEventTime < MAX_TIME );
|
||||
|
||||
// This relies on the fact that z is a sentry with a maximal m_rtEventTime
|
||||
for(;;p_prev = p_n)
|
||||
{
|
||||
p_n = p_prev->m_next;
|
||||
if ( p_n->m_rtEventTime > pPacket->m_rtEventTime ) break;
|
||||
}
|
||||
// If p_prev == pPacket then we're already in the right place
|
||||
if (p_prev != pPacket)
|
||||
{
|
||||
head.m_next = pPacket->m_next;
|
||||
(p_prev->m_next = pPacket)->m_next = p_n;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
DbgLog((LOG_TIMING, 2, TEXT("Periodic advise %lu, shunted to %lu"),
|
||||
pPacket->m_dwAdviseCookie, (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) ));
|
||||
#endif
|
||||
m_Serialize.Unlock();
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
void CAMSchedule::DumpLinkedList()
|
||||
{
|
||||
m_Serialize.Lock();
|
||||
int i=0;
|
||||
DbgLog((LOG_TIMING, 1, TEXT("CAMSchedule::DumpLinkedList() this = 0x%p"), this));
|
||||
for ( CAdvisePacket * p = &head
|
||||
; p
|
||||
; p = p->m_next , i++
|
||||
)
|
||||
{
|
||||
DbgLog((LOG_TIMING, 1, TEXT("Advise List # %lu, Cookie %d, RefTime %lu"),
|
||||
i,
|
||||
p->m_dwAdviseCookie,
|
||||
p->m_rtEventTime / (UNITS / MILLISECONDS)
|
||||
));
|
||||
}
|
||||
m_Serialize.Unlock();
|
||||
}
|
||||
#endif
|
@ -1,128 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Schedule.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __CAMSchedule__
|
||||
#define __CAMSchedule__
|
||||
|
||||
class CAMSchedule : private CBaseObject
|
||||
{
|
||||
public:
|
||||
virtual ~CAMSchedule();
|
||||
// ev is the event we should fire if the advise time needs re-evaluating
|
||||
CAMSchedule( HANDLE ev );
|
||||
|
||||
DWORD GetAdviseCount();
|
||||
REFERENCE_TIME GetNextAdviseTime();
|
||||
|
||||
// We need a method for derived classes to add advise packets, we return the cookie
|
||||
DWORD_PTR AddAdvisePacket( const REFERENCE_TIME & time1, const REFERENCE_TIME & time2, HANDLE h, BOOL periodic );
|
||||
// And a way to cancel
|
||||
HRESULT Unadvise(DWORD_PTR dwAdviseCookie);
|
||||
|
||||
// Tell us the time please, and we'll dispatch the expired events. We return the time of the next event.
|
||||
// NB: The time returned will be "useless" if you start adding extra Advises. But that's the problem of
|
||||
// whoever is using this helper class (typically a clock).
|
||||
REFERENCE_TIME Advise( const REFERENCE_TIME & rtTime );
|
||||
|
||||
// Get the event handle which will be set if advise time requires re-evaluation.
|
||||
HANDLE GetEvent() const { return m_ev; }
|
||||
|
||||
private:
|
||||
// We define the nodes that will be used in our singly linked list
|
||||
// of advise packets. The list is ordered by time, with the
|
||||
// elements that will expire first at the front.
|
||||
class CAdvisePacket
|
||||
{
|
||||
public:
|
||||
CAdvisePacket()
|
||||
{}
|
||||
|
||||
CAdvisePacket * m_next;
|
||||
DWORD_PTR m_dwAdviseCookie;
|
||||
REFERENCE_TIME m_rtEventTime; // Time at which event should be set
|
||||
REFERENCE_TIME m_rtPeriod; // Periodic time
|
||||
HANDLE m_hNotify; // Handle to event or semephore
|
||||
BOOL m_bPeriodic; // TRUE => Periodic event
|
||||
|
||||
CAdvisePacket( CAdvisePacket * next, LONGLONG time ) : m_next(next), m_rtEventTime(time)
|
||||
{}
|
||||
|
||||
void InsertAfter( CAdvisePacket * p )
|
||||
{
|
||||
p->m_next = m_next;
|
||||
m_next = p;
|
||||
}
|
||||
|
||||
int IsZ() const // That is, is it the node that represents the end of the list
|
||||
{ return m_next == 0; }
|
||||
|
||||
CAdvisePacket * RemoveNext()
|
||||
{
|
||||
CAdvisePacket *const next = m_next;
|
||||
CAdvisePacket *const new_next = next->m_next;
|
||||
m_next = new_next;
|
||||
return next;
|
||||
}
|
||||
|
||||
void DeleteNext()
|
||||
{
|
||||
delete RemoveNext();
|
||||
}
|
||||
|
||||
CAdvisePacket * Next() const
|
||||
{
|
||||
CAdvisePacket * result = m_next;
|
||||
if (result->IsZ()) result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD_PTR Cookie() const
|
||||
{ return m_dwAdviseCookie; }
|
||||
};
|
||||
|
||||
// Structure is:
|
||||
// head -> elmt1 -> elmt2 -> z -> null
|
||||
// So an empty list is: head -> z -> null
|
||||
// Having head & z as links makes insertaion,
|
||||
// deletion and shunting much easier.
|
||||
CAdvisePacket head, z; // z is both a tail and a sentry
|
||||
|
||||
volatile DWORD_PTR m_dwNextCookie; // Strictly increasing
|
||||
volatile DWORD m_dwAdviseCount; // Number of elements on list
|
||||
|
||||
CCritSec m_Serialize;
|
||||
|
||||
// AddAdvisePacket: adds the packet, returns the cookie (0 if failed)
|
||||
DWORD_PTR AddAdvisePacket( CAdvisePacket * pPacket );
|
||||
// Event that we should set if the packed added above will be the next to fire.
|
||||
const HANDLE m_ev;
|
||||
|
||||
// A Shunt is where we have changed the first element in the
|
||||
// list and want it re-evaluating (i.e. repositioned) in
|
||||
// the list.
|
||||
void ShuntHead();
|
||||
|
||||
// Rather than delete advise packets, we cache them for future use
|
||||
CAdvisePacket * m_pAdviseCache;
|
||||
DWORD m_dwCacheCount;
|
||||
enum { dwCacheMax = 5 }; // Don't bother caching more than five
|
||||
|
||||
void Delete( CAdvisePacket * pLink );// This "Delete" will cache the Link
|
||||
|
||||
// Attributes and methods for debugging
|
||||
public:
|
||||
#ifdef DEBUG
|
||||
void DumpLinkedList();
|
||||
#else
|
||||
void DumpLinkedList() {}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // __CAMSchedule__
|
@ -1,83 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: SeekPT.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 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("Seeking PassThru"),pUnk, phr);
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CSeekingPassThru::NonDelegatingQueryInterface(REFIID riid, void ** ppv)
|
||||
{
|
||||
if (riid == IID_ISeekingPassThru) {
|
||||
return GetInterface((ISeekingPassThru *) this, ppv);
|
||||
} else {
|
||||
if (m_pPosPassThru &&
|
||||
(riid == IID_IMediaSeeking ||
|
||||
riid == IID_IMediaPosition)) {
|
||||
return m_pPosPassThru->NonDelegatingQueryInterface(riid,ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CSeekingPassThru::CSeekingPassThru( TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr )
|
||||
: CUnknown(pName, pUnk, phr),
|
||||
m_pPosPassThru(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CSeekingPassThru::~CSeekingPassThru()
|
||||
{
|
||||
delete m_pPosPassThru;
|
||||
}
|
||||
|
||||
STDMETHODIMP CSeekingPassThru::Init(BOOL bRendererSeeking, IPin *pPin)
|
||||
{
|
||||
HRESULT hr = NOERROR;
|
||||
if (m_pPosPassThru) {
|
||||
hr = E_FAIL;
|
||||
} else {
|
||||
m_pPosPassThru =
|
||||
bRendererSeeking ?
|
||||
new CRendererPosPassThru(
|
||||
NAME("Render Seeking COM object"),
|
||||
(IUnknown *)this,
|
||||
&hr,
|
||||
pPin) :
|
||||
new CPosPassThru(
|
||||
NAME("Render Seeking COM object"),
|
||||
(IUnknown *)this,
|
||||
&hr,
|
||||
pPin);
|
||||
if (!m_pPosPassThru) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
} else {
|
||||
if (FAILED(hr)) {
|
||||
delete m_pPosPassThru;
|
||||
m_pPosPassThru = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: SeekPT.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __seekpt_h__
|
||||
#define __seekpt_h__
|
||||
|
||||
|
||||
class CSeekingPassThru : public ISeekingPassThru, public CUnknown
|
||||
{
|
||||
public:
|
||||
static CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
|
||||
CSeekingPassThru(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr);
|
||||
~CSeekingPassThru();
|
||||
|
||||
DECLARE_IUNKNOWN;
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
|
||||
|
||||
STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin);
|
||||
|
||||
private:
|
||||
CPosPassThru *m_pPosPassThru;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,522 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Source.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements CSource, which is a Quartz
|
||||
// source filter 'template.'
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Locking Strategy.
|
||||
//
|
||||
// Hold the filter critical section (m_pFilter->pStateLock()) to serialise
|
||||
// access to functions. Note that, in general, this lock may be held
|
||||
// by a function when the worker thread may want to hold it. Therefore
|
||||
// if you wish to access shared state from the worker thread you will
|
||||
// need to add another critical section object. The execption is during
|
||||
// the threads processing loop, when it is safe to get the filter critical
|
||||
// section from within FillBuffer().
|
||||
|
||||
#include "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);
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid)
|
||||
: CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
|
||||
m_iPins(0),
|
||||
m_paStreams(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr)
|
||||
: CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
|
||||
m_iPins(0),
|
||||
m_paStreams(NULL)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(phr);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// CSource::Destructor
|
||||
//
|
||||
CSource::~CSource()
|
||||
{
|
||||
/* Free our pins and pin array */
|
||||
while (m_iPins != 0) {
|
||||
// deleting the pins causes them to be removed from the array...
|
||||
delete m_paStreams[m_iPins - 1];
|
||||
}
|
||||
|
||||
ASSERT(m_paStreams == NULL);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Add a new pin
|
||||
//
|
||||
HRESULT CSource::AddPin(CSourceStream *pStream)
|
||||
{
|
||||
CAutoLock lock(&m_cStateLock);
|
||||
|
||||
/* Allocate space for this pin and the old ones */
|
||||
CSourceStream **paStreams = new CSourceStream *[m_iPins + 1];
|
||||
if (paStreams == NULL) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
if (m_paStreams != NULL) {
|
||||
CopyMemory((PVOID)paStreams, (PVOID)m_paStreams,
|
||||
m_iPins * sizeof(m_paStreams[0]));
|
||||
paStreams[m_iPins] = pStream;
|
||||
delete [] m_paStreams;
|
||||
}
|
||||
m_paStreams = paStreams;
|
||||
m_paStreams[m_iPins] = pStream;
|
||||
m_iPins++;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// Remove a pin - pStream is NOT deleted
|
||||
//
|
||||
HRESULT CSource::RemovePin(CSourceStream *pStream)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < m_iPins; i++) {
|
||||
if (m_paStreams[i] == pStream) {
|
||||
if (m_iPins == 1) {
|
||||
delete [] m_paStreams;
|
||||
m_paStreams = NULL;
|
||||
} else {
|
||||
/* no need to reallocate */
|
||||
while (++i < m_iPins)
|
||||
m_paStreams[i - 1] = m_paStreams[i];
|
||||
}
|
||||
m_iPins--;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// FindPin
|
||||
//
|
||||
// Set *ppPin to the IPin* that has the id Id.
|
||||
// or to NULL if the Id cannot be matched.
|
||||
STDMETHODIMP CSource::FindPin(LPCWSTR Id, IPin **ppPin)
|
||||
{
|
||||
CheckPointer(ppPin,E_POINTER);
|
||||
ValidateReadWritePtr(ppPin,sizeof(IPin *));
|
||||
// The -1 undoes the +1 in QueryId and ensures that totally invalid
|
||||
// strings (for which WstrToInt delivers 0) give a deliver a NULL pin.
|
||||
int i = WstrToInt(Id) -1;
|
||||
*ppPin = GetPin(i);
|
||||
if (*ppPin!=NULL){
|
||||
(*ppPin)->AddRef();
|
||||
return NOERROR;
|
||||
} else {
|
||||
return VFW_E_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FindPinNumber
|
||||
//
|
||||
// return the number of the pin with this IPin* or -1 if none
|
||||
int CSource::FindPinNumber(IPin *iPin) {
|
||||
int i;
|
||||
for (i=0; 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 an invalid pin
|
||||
int i = 1+ m_pFilter->FindPinNumber(this);
|
||||
if (i<1) return VFW_E_NOT_FOUND;
|
||||
*Id = (LPWSTR)CoTaskMemAlloc(4*sizeof(WCHAR));
|
||||
if (*Id==NULL) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
IntToWstr(i, *Id, 4);
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CSourceStream::Constructor
|
||||
//
|
||||
// increments the number of pins present on the filter
|
||||
CSourceStream::CSourceStream(
|
||||
TCHAR *pObjectName,
|
||||
HRESULT *phr,
|
||||
CSource *ps,
|
||||
LPCWSTR pPinName)
|
||||
: CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
|
||||
m_pFilter(ps) {
|
||||
|
||||
*phr = m_pFilter->AddPin(this);
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
CSourceStream::CSourceStream(
|
||||
char *pObjectName,
|
||||
HRESULT *phr,
|
||||
CSource *ps,
|
||||
LPCWSTR pPinName)
|
||||
: CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
|
||||
m_pFilter(ps) {
|
||||
|
||||
*phr = m_pFilter->AddPin(this);
|
||||
}
|
||||
#endif
|
||||
//
|
||||
// CSourceStream::Destructor
|
||||
//
|
||||
// Decrements the number of pins on this filter
|
||||
CSourceStream::~CSourceStream(void) {
|
||||
|
||||
m_pFilter->RemovePin(this);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CheckMediaType
|
||||
//
|
||||
// Do we support this type? Provides the default support for 1 type.
|
||||
HRESULT CSourceStream::CheckMediaType(const CMediaType *pMediaType) {
|
||||
|
||||
CAutoLock lock(m_pFilter->pStateLock());
|
||||
|
||||
CMediaType mt;
|
||||
GetMediaType(&mt);
|
||||
|
||||
if (mt == *pMediaType) {
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// GetMediaType/3
|
||||
//
|
||||
// By default we support only one type
|
||||
// iPosition indexes are 0-n
|
||||
HRESULT CSourceStream::GetMediaType(int iPosition, CMediaType *pMediaType) {
|
||||
|
||||
CAutoLock lock(m_pFilter->pStateLock());
|
||||
|
||||
if (iPosition<0) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (iPosition>0) {
|
||||
return VFW_S_NO_MORE_ITEMS;
|
||||
}
|
||||
return GetMediaType(pMediaType);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Active
|
||||
//
|
||||
// The pin is active - start up the worker thread
|
||||
HRESULT CSourceStream::Active(void) {
|
||||
|
||||
CAutoLock lock(m_pFilter->pStateLock());
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
if (m_pFilter->IsActive()) {
|
||||
return S_FALSE; // succeeded, but did not allocate resources (they already exist...)
|
||||
}
|
||||
|
||||
// do nothing if not connected - its ok not to connect to
|
||||
// all pins of a source filter
|
||||
if (!IsConnected()) {
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
hr = CBaseOutputPin::Active();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
ASSERT(!ThreadExists());
|
||||
|
||||
// start the thread
|
||||
if (!Create()) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Tell thread to initialize. If OnThreadCreate Fails, so does this.
|
||||
hr = Init();
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
return Pause();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Inactive
|
||||
//
|
||||
// Pin is inactive - shut down the worker thread
|
||||
// Waits for the worker to exit before returning.
|
||||
HRESULT CSourceStream::Inactive(void) {
|
||||
|
||||
CAutoLock lock(m_pFilter->pStateLock());
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
// do nothing if not connected - its ok not to connect to
|
||||
// all pins of a source filter
|
||||
if (!IsConnected()) {
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
// !!! need to do this before trying to stop the thread, because
|
||||
// we may be stuck waiting for our own allocator!!!
|
||||
|
||||
hr = CBaseOutputPin::Inactive(); // call this first to Decommit the allocator
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (ThreadExists()) {
|
||||
hr = Stop();
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = Exit();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
Close(); // Wait for the thread to exit, then tidy up.
|
||||
}
|
||||
|
||||
// hr = CBaseOutputPin::Inactive(); // call this first to Decommit the allocator
|
||||
//if (FAILED(hr)) {
|
||||
// return hr;
|
||||
//}
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ThreadProc
|
||||
//
|
||||
// When this returns the thread exits
|
||||
// Return codes > 0 indicate an error occured
|
||||
DWORD CSourceStream::ThreadProc(void) {
|
||||
|
||||
HRESULT hr; // the return code from calls
|
||||
Command com;
|
||||
|
||||
do {
|
||||
com = GetRequest();
|
||||
if (com != CMD_INIT) {
|
||||
DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command")));
|
||||
Reply((DWORD) E_UNEXPECTED);
|
||||
}
|
||||
} while (com != CMD_INIT);
|
||||
|
||||
DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread initializing")));
|
||||
|
||||
hr = OnThreadCreate(); // perform set up tasks
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));
|
||||
OnThreadDestroy();
|
||||
Reply(hr); // send failed return code from OnThreadCreate
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialisation suceeded
|
||||
Reply(NOERROR);
|
||||
|
||||
Command cmd;
|
||||
do {
|
||||
cmd = GetRequest();
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case CMD_EXIT:
|
||||
Reply(NOERROR);
|
||||
break;
|
||||
|
||||
case CMD_RUN:
|
||||
DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));
|
||||
// !!! fall through???
|
||||
|
||||
case CMD_PAUSE:
|
||||
Reply(NOERROR);
|
||||
DoBufferProcessingLoop();
|
||||
break;
|
||||
|
||||
case CMD_STOP:
|
||||
Reply(NOERROR);
|
||||
break;
|
||||
|
||||
default:
|
||||
DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd));
|
||||
Reply((DWORD) E_NOTIMPL);
|
||||
break;
|
||||
}
|
||||
} while (cmd != CMD_EXIT);
|
||||
|
||||
hr = OnThreadDestroy(); // tidy up.
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting")));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DoBufferProcessingLoop
|
||||
//
|
||||
// Grabs a buffer and calls the users processing function.
|
||||
// Overridable, so that different delivery styles can be catered for.
|
||||
HRESULT CSourceStream::DoBufferProcessingLoop(void) {
|
||||
|
||||
Command com;
|
||||
|
||||
OnThreadStartPlay();
|
||||
|
||||
do {
|
||||
while (!CheckRequest(&com)) {
|
||||
|
||||
IMediaSample *pSample;
|
||||
|
||||
HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);
|
||||
if (FAILED(hr)) {
|
||||
Sleep(1);
|
||||
continue; // go round again. Perhaps the error will go away
|
||||
// or the allocator is decommited & we will be asked to
|
||||
// exit soon.
|
||||
}
|
||||
|
||||
// Virtual function user will override.
|
||||
hr = FillBuffer(pSample);
|
||||
|
||||
if (hr == S_OK) {
|
||||
hr = Deliver(pSample);
|
||||
pSample->Release();
|
||||
|
||||
// downstream filter returns S_FALSE if it wants us to
|
||||
// stop or an error if it's reporting an error.
|
||||
if(hr != S_OK)
|
||||
{
|
||||
DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
} else if (hr == S_FALSE) {
|
||||
// derived class wants us to stop pushing data
|
||||
pSample->Release();
|
||||
DeliverEndOfStream();
|
||||
return S_OK;
|
||||
} else {
|
||||
// derived class encountered an error
|
||||
pSample->Release();
|
||||
DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));
|
||||
DeliverEndOfStream();
|
||||
m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// all paths release the sample
|
||||
}
|
||||
|
||||
// For all commands sent to us there must be a Reply call!
|
||||
|
||||
if (com == CMD_RUN || com == CMD_PAUSE) {
|
||||
Reply(NOERROR);
|
||||
} else if (com != CMD_STOP) {
|
||||
Reply((DWORD) E_UNEXPECTED);
|
||||
DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));
|
||||
}
|
||||
} while (com != CMD_STOP);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
@ -1,172 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Source.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines classes to simplify creation of
|
||||
// ActiveX source filters that support continuous generation of data.
|
||||
// No support is provided for IMediaControl or IMediaPosition.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// Derive your source filter from CSource.
|
||||
// During construction either:
|
||||
// Create some CSourceStream objects to manage your pins
|
||||
// Provide the user with a means of doing so eg, an IPersistFile interface.
|
||||
//
|
||||
// CSource provides:
|
||||
// IBaseFilter interface management
|
||||
// IMediaFilter interface management, via CBaseFilter
|
||||
// Pin counting for CBaseFilter
|
||||
//
|
||||
// Derive a class from CSourceStream to manage your output pin types
|
||||
// Implement GetMediaType/1 to return the type you support. If you support multiple
|
||||
// types then overide GetMediaType/3, CheckMediaType and GetMediaTypeCount.
|
||||
// Implement Fillbuffer() to put data into one buffer.
|
||||
//
|
||||
// CSourceStream provides:
|
||||
// IPin management via CBaseOutputPin
|
||||
// Worker thread management
|
||||
|
||||
#ifndef __CSOURCE__
|
||||
#define __CSOURCE__
|
||||
|
||||
class CSourceStream; // The class that will handle each pin
|
||||
|
||||
|
||||
//
|
||||
// CSource
|
||||
//
|
||||
// Override construction to provide a means of creating
|
||||
// CSourceStream derived objects - ie a way of creating pins.
|
||||
class CSource : public CBaseFilter {
|
||||
public:
|
||||
|
||||
CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr);
|
||||
CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid);
|
||||
#ifdef UNICODE
|
||||
CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr);
|
||||
CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid);
|
||||
#endif
|
||||
~CSource();
|
||||
|
||||
int GetPinCount(void);
|
||||
CBasePin *GetPin(int n);
|
||||
|
||||
// -- Utilities --
|
||||
|
||||
CCritSec* pStateLock(void) { return &m_cStateLock; } // provide our critical section
|
||||
|
||||
HRESULT AddPin(CSourceStream *);
|
||||
HRESULT RemovePin(CSourceStream *);
|
||||
|
||||
STDMETHODIMP FindPin(
|
||||
LPCWSTR Id,
|
||||
IPin ** ppPin
|
||||
);
|
||||
|
||||
int FindPinNumber(IPin *iPin);
|
||||
|
||||
protected:
|
||||
|
||||
int m_iPins; // The number of pins on this filter. Updated by CSourceStream
|
||||
// constructors & destructors.
|
||||
CSourceStream **m_paStreams; // the pins on this filter.
|
||||
|
||||
CCritSec m_cStateLock; // Lock this to serialize function accesses to the filter state
|
||||
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// CSourceStream
|
||||
//
|
||||
// Use this class to manage a stream of data that comes from a
|
||||
// pin.
|
||||
// Uses a worker thread to put data on the pin.
|
||||
class CSourceStream : public CAMThread, public CBaseOutputPin {
|
||||
public:
|
||||
|
||||
CSourceStream(TCHAR *pObjectName,
|
||||
HRESULT *phr,
|
||||
CSource *pms,
|
||||
LPCWSTR pName);
|
||||
#ifdef UNICODE
|
||||
CSourceStream(CHAR *pObjectName,
|
||||
HRESULT *phr,
|
||||
CSource *pms,
|
||||
LPCWSTR pName);
|
||||
#endif
|
||||
virtual ~CSourceStream(void); // virtual destructor ensures derived class destructors are called too.
|
||||
|
||||
protected:
|
||||
|
||||
CSource *m_pFilter; // The parent of this stream
|
||||
|
||||
// *
|
||||
// * Data Source
|
||||
// *
|
||||
// * The following three functions: FillBuffer, OnThreadCreate/Destroy, are
|
||||
// * called from within the ThreadProc. They are used in the creation of
|
||||
// * the media samples this pin will provide
|
||||
// *
|
||||
|
||||
// Override this to provide the worker thread a means
|
||||
// of processing a buffer
|
||||
virtual HRESULT FillBuffer(IMediaSample *pSamp) PURE;
|
||||
|
||||
// Called as the thread is created/destroyed - use to perform
|
||||
// jobs such as start/stop streaming mode
|
||||
// If OnThreadCreate returns an error the thread will exit.
|
||||
virtual HRESULT OnThreadCreate(void) {return NOERROR;};
|
||||
virtual HRESULT OnThreadDestroy(void) {return NOERROR;};
|
||||
virtual HRESULT OnThreadStartPlay(void) {return NOERROR;};
|
||||
|
||||
// *
|
||||
// * Worker Thread
|
||||
// *
|
||||
|
||||
HRESULT Active(void); // Starts up the worker thread
|
||||
HRESULT Inactive(void); // Exits the worker thread.
|
||||
|
||||
public:
|
||||
// thread commands
|
||||
enum Command {CMD_INIT, CMD_PAUSE, CMD_RUN, CMD_STOP, CMD_EXIT};
|
||||
HRESULT Init(void) { return CallWorker(CMD_INIT); }
|
||||
HRESULT Exit(void) { return CallWorker(CMD_EXIT); }
|
||||
HRESULT Run(void) { return CallWorker(CMD_RUN); }
|
||||
HRESULT Pause(void) { return CallWorker(CMD_PAUSE); }
|
||||
HRESULT Stop(void) { return CallWorker(CMD_STOP); }
|
||||
|
||||
protected:
|
||||
Command GetRequest(void) { return (Command) CAMThread::GetRequest(); }
|
||||
BOOL CheckRequest(Command *pCom) { return CAMThread::CheckRequest( (DWORD *) pCom); }
|
||||
|
||||
// override these if you want to add thread commands
|
||||
virtual DWORD ThreadProc(void); // the thread function
|
||||
|
||||
virtual HRESULT DoBufferProcessingLoop(void); // the loop executed whilst running
|
||||
|
||||
|
||||
// *
|
||||
// * AM_MEDIA_TYPE support
|
||||
// *
|
||||
|
||||
// If you support more than one media type then override these 2 functions
|
||||
virtual HRESULT CheckMediaType(const CMediaType *pMediaType);
|
||||
virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType); // List pos. 0-n
|
||||
|
||||
// If you support only one type then override this fn.
|
||||
// This will only be called by the default implementations
|
||||
// of CheckMediaType and GetMediaType(int, CMediaType*)
|
||||
// You must override this fn. or the above 2!
|
||||
virtual HRESULT GetMediaType(CMediaType *pMediaType) {return E_UNEXPECTED;}
|
||||
|
||||
STDMETHODIMP QueryId(
|
||||
LPWSTR * Id
|
||||
);
|
||||
};
|
||||
|
||||
#endif // __CSOURCE__
|
||||
|
@ -1,253 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Streams.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines overall streams architecture.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __STREAMS__
|
||||
#define __STREAMS__
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// disable some level-4 warnings, use #pragma warning(enable:###) to re-enable
|
||||
#pragma warning(disable:4100) // warning C4100: unreferenced formal parameter
|
||||
#pragma warning(disable:4127) // warning C4127: conditional expression is constant
|
||||
#pragma warning(disable:4189) // warning C4189: local variable is initialized but not referenced
|
||||
#pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
|
||||
#pragma warning(disable:4511) // warning C4511: copy constructor could not be generated
|
||||
#pragma warning(disable:4512) // warning C4512: assignment operator could not be generated
|
||||
#pragma warning(disable:4514) // warning C4514: unreferenced inline function has been removed
|
||||
#pragma warning(disable:4710) // warning C4710: 'function' not inlined
|
||||
|
||||
#if _MSC_VER>=1100
|
||||
#define AM_NOVTABLE __declspec(novtable)
|
||||
#else
|
||||
#define AM_NOVTABLE
|
||||
#endif
|
||||
#endif // MSC_VER
|
||||
|
||||
// Because of differences between Visual C++ and older Microsoft SDKs,
|
||||
// you may have defined _DEBUG without defining DEBUG. This logic
|
||||
// ensures that both will be set if Visual C++ sets _DEBUG.
|
||||
#ifdef _DEBUG
|
||||
#ifndef DEBUG
|
||||
#define DEBUG
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <olectl.h>
|
||||
|
||||
// Disable warning message for C4201 - use of nameless struct/union
|
||||
// Otherwise, strmif.h will generate warnings for Win32 debug builds
|
||||
#pragma warning( disable : 4201 )
|
||||
|
||||
#include <mmsystem.h>
|
||||
|
||||
#ifndef NUMELMS
|
||||
#define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// The following definitions come from the Platform SDK and are required if
|
||||
// the applicaiton is being compiled with the headers from Visual C++ 6.0.
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
#ifndef InterlockedExchangePointer
|
||||
#define InterlockedExchangePointer(Target, Value) \
|
||||
(PVOID)InterlockedExchange((PLONG)(Target), (LONG)(Value))
|
||||
#endif
|
||||
|
||||
#ifndef _WAVEFORMATEXTENSIBLE_
|
||||
#define _WAVEFORMATEXTENSIBLE_
|
||||
typedef struct {
|
||||
WAVEFORMATEX Format;
|
||||
union {
|
||||
WORD wValidBitsPerSample; /* bits of precision */
|
||||
WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
|
||||
WORD wReserved; /* If neither applies, set to zero. */
|
||||
} Samples;
|
||||
DWORD dwChannelMask; /* which channels are */
|
||||
/* present in stream */
|
||||
GUID SubFormat;
|
||||
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
|
||||
#endif // !_WAVEFORMATEXTENSIBLE_
|
||||
|
||||
#if !defined(WAVE_FORMAT_EXTENSIBLE)
|
||||
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
|
||||
#endif // !defined(WAVE_FORMAT_EXTENSIBLE)
|
||||
|
||||
#ifndef GetWindowLongPtr
|
||||
#define GetWindowLongPtrA GetWindowLongA
|
||||
#define GetWindowLongPtrW GetWindowLongW
|
||||
#ifdef UNICODE
|
||||
#define GetWindowLongPtr GetWindowLongPtrW
|
||||
#else
|
||||
#define GetWindowLongPtr GetWindowLongPtrA
|
||||
#endif // !UNICODE
|
||||
#endif // !GetWindowLongPtr
|
||||
|
||||
#ifndef SetWindowLongPtr
|
||||
#define SetWindowLongPtrA SetWindowLongA
|
||||
#define SetWindowLongPtrW SetWindowLongW
|
||||
#ifdef UNICODE
|
||||
#define SetWindowLongPtr SetWindowLongPtrW
|
||||
#else
|
||||
#define SetWindowLongPtr SetWindowLongPtrA
|
||||
#endif // !UNICODE
|
||||
#endif // !SetWindowLongPtr
|
||||
|
||||
#ifndef GWLP_WNDPROC
|
||||
#define GWLP_WNDPROC (-4)
|
||||
#endif
|
||||
#ifndef GWLP_HINSTANCE
|
||||
#define GWLP_HINSTANCE (-6)
|
||||
#endif
|
||||
#ifndef GWLP_HWNDPARENT
|
||||
#define GWLP_HWNDPARENT (-8)
|
||||
#endif
|
||||
#ifndef GWLP_USERDATA
|
||||
#define GWLP_USERDATA (-21)
|
||||
#endif
|
||||
#ifndef GWLP_ID
|
||||
#define GWLP_ID (-12)
|
||||
#endif
|
||||
#ifndef DWLP_MSGRESULT
|
||||
#define DWLP_MSGRESULT 0
|
||||
#endif
|
||||
#ifndef DWLP_DLGPROC
|
||||
#define DWLP_DLGPROC DWLP_MSGRESULT + sizeof(LRESULT)
|
||||
#endif
|
||||
#ifndef DWLP_USER
|
||||
#define DWLP_USER DWLP_DLGPROC + sizeof(DLGPROC)
|
||||
#endif
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// End Platform SDK definitions
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <initguid.h>
|
||||
|
||||
#pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
|
||||
#include <strmif.h> // Generated IDL header file for streams interfaces
|
||||
|
||||
#include "reftime.h" // Helper class for REFERENCE_TIME management
|
||||
#include "wxdebug.h" // Debug support for logging and ASSERTs
|
||||
#include "amvideo.h" // ActiveMovie video interfaces and definitions
|
||||
//include amaudio.h explicitly if you need it. it requires the DirectX SDK.
|
||||
//#include "amaudio.h" // ActiveMovie audio interfaces and definitions
|
||||
#include "wxutil.h" // General helper classes for threads etc
|
||||
#include "combase.h" // Base COM classes to support IUnknown
|
||||
//#include "dllsetup.h" // Filter registration support functions
|
||||
#include "measure.h" // Performance measurement
|
||||
#include "comlite.h" // Light weight com function prototypes
|
||||
|
||||
#include "cache.h" // Simple cache container class
|
||||
#include "wxlist.h" // Non MFC generic list class
|
||||
#include "msgthrd.h" // CMsgThread
|
||||
#include "mtype.h" // Helper class for managing media types
|
||||
#include "fourcc.h" // conversions between FOURCCs and GUIDs
|
||||
#include "control.h" // generated from control.odl
|
||||
#include "ctlutil.h" // control interface utility classes
|
||||
#include "evcode.h" // event code definitions
|
||||
#include "amfilter.h" // Main streams architecture class hierachy
|
||||
#include "transfrm.h" // Generic transform filter
|
||||
#include "transip.h" // Generic transform-in-place filter
|
||||
#include "uuids.h" // declaration of type GUIDs and well-known clsids
|
||||
#include "source.h" // Generic source filter
|
||||
#include "outputq.h" // Output pin queueing
|
||||
#include "errors.h" // HRESULT status and error definitions
|
||||
#include "renbase.h" // Base class for writing ActiveX renderers
|
||||
//#include "winutil.h" // Helps with filters that manage windows
|
||||
//#include "winctrl.h" // Implements the IVideoWindow interface
|
||||
//#include "videoctl.h" // Specifically video related classes
|
||||
#include "refclock.h" // Base clock class
|
||||
#include "sysclock.h" // System clock
|
||||
#include "pstream.h" // IPersistStream helper class
|
||||
#include "vtrans.h" // Video Transform Filter base class
|
||||
#include "amextra.h"
|
||||
//#include "cprop.h" // Base property page class
|
||||
#include "strmctl.h" // IAMStreamControl support
|
||||
#include "edevdefs.h" // External device control interface defines
|
||||
#include "audevcod.h" // audio filter device error event codes
|
||||
|
||||
#include <tchar.h>
|
||||
|
||||
#define NO_SHLWAPI_STRFCNS
|
||||
//#include <atlbase.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#ifndef NUMELMS
|
||||
#define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
|
||||
#endif
|
||||
|
||||
class CPinInfo : public PIN_INFO
|
||||
{
|
||||
public:
|
||||
CPinInfo() {pFilter = NULL;}
|
||||
~CPinInfo() {if(pFilter) pFilter->Release();}
|
||||
};
|
||||
|
||||
class CFilterInfo : public FILTER_INFO
|
||||
{
|
||||
public:
|
||||
CFilterInfo() {pGraph = NULL;}
|
||||
~CFilterInfo() {if(pGraph) pGraph->Release();}
|
||||
};
|
||||
|
||||
#define BeginEnumFilters(pFilterGraph, pEnumFilters, pBaseFilter) \
|
||||
{CComPtr<IEnumFilters> pEnumFilters; \
|
||||
if(pFilterGraph && SUCCEEDED(pFilterGraph->EnumFilters(&pEnumFilters))) \
|
||||
{ \
|
||||
for(CComPtr<IBaseFilter> pBaseFilter; S_OK == pEnumFilters->Next(1, &pBaseFilter, 0); pBaseFilter = NULL) \
|
||||
{ \
|
||||
|
||||
#define EndEnumFilters }}}
|
||||
|
||||
#define BeginEnumCachedFilters(pGraphConfig, pEnumFilters, pBaseFilter) \
|
||||
{CComPtr<IEnumFilters> pEnumFilters; \
|
||||
if(pGraphConfig && SUCCEEDED(pGraphConfig->EnumCacheFilter(&pEnumFilters))) \
|
||||
{ \
|
||||
for(CComPtr<IBaseFilter> pBaseFilter; S_OK == pEnumFilters->Next(1, &pBaseFilter, 0); pBaseFilter = NULL) \
|
||||
{ \
|
||||
|
||||
#define EndEnumCachedFilters }}}
|
||||
|
||||
#define BeginEnumPins(pBaseFilter, pEnumPins, pPin) \
|
||||
{CComPtr<IEnumPins> pEnumPins; \
|
||||
if(pBaseFilter && SUCCEEDED(pBaseFilter->EnumPins(&pEnumPins))) \
|
||||
{ \
|
||||
for(CComPtr<IPin> pPin; S_OK == pEnumPins->Next(1, &pPin, 0); pPin = NULL) \
|
||||
{ \
|
||||
|
||||
#define EndEnumPins }}}
|
||||
|
||||
#define BeginEnumMediaTypes(pPin, pEnumMediaTypes, pMediaType) \
|
||||
{CComPtr<IEnumMediaTypes> pEnumMediaTypes; \
|
||||
if(pPin && SUCCEEDED(pPin->EnumMediaTypes(&pEnumMediaTypes))) \
|
||||
{ \
|
||||
AM_MEDIA_TYPE* pMediaType = NULL; \
|
||||
for(; S_OK == pEnumMediaTypes->Next(1, &pMediaType, NULL); DeleteMediaType(pMediaType), pMediaType = NULL) \
|
||||
{ \
|
||||
|
||||
#define EndEnumMediaTypes(pMediaType) } if(pMediaType) DeleteMediaType(pMediaType); }}
|
||||
|
||||
#define BeginEnumSysDev(clsid, pMoniker) \
|
||||
{CComPtr<ICreateDevEnum> pDevEnum4$##clsid; \
|
||||
pDevEnum4$##clsid.CoCreateInstance(CLSID_SystemDeviceEnum); \
|
||||
CComPtr<IEnumMoniker> pClassEnum4$##clsid; \
|
||||
if(SUCCEEDED(pDevEnum4$##clsid->CreateClassEnumerator(clsid, &pClassEnum4$##clsid, 0)) \
|
||||
&& pClassEnum4$##clsid) \
|
||||
{ \
|
||||
for(CComPtr<IMoniker> pMoniker; pClassEnum4$##clsid->Next(1, &pMoniker, 0) == S_OK; pMoniker = NULL) \
|
||||
{ \
|
||||
|
||||
#define EndEnumSysDev }}}
|
||||
|
||||
#else
|
||||
#ifdef DEBUG
|
||||
#pragma message("STREAMS.H included TWICE")
|
||||
#endif
|
||||
#endif // __STREAMS__
|
||||
|
@ -1,401 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: StrmCtl.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include "streams.h"
|
||||
#include "strmctl.h"
|
||||
|
||||
CBaseStreamControl::CBaseStreamControl()
|
||||
: m_StreamState(STREAM_FLOWING)
|
||||
, m_StreamStateOnStop(STREAM_FLOWING) // means no pending stop
|
||||
, m_tStartTime(MAX_TIME)
|
||||
, m_tStopTime(MAX_TIME)
|
||||
, m_dwStartCookie(0)
|
||||
, m_dwStopCookie(0)
|
||||
, m_pRefClock(NULL)
|
||||
, m_FilterState(State_Stopped)
|
||||
, m_bIsFlushing(FALSE)
|
||||
, m_bStopSendExtra(FALSE)
|
||||
{}
|
||||
|
||||
CBaseStreamControl::~CBaseStreamControl()
|
||||
{
|
||||
// Make sure we release the clock.
|
||||
SetSyncSource(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CBaseStreamControl::StopAt(const REFERENCE_TIME * ptStop, BOOL bSendExtra, DWORD dwCookie)
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
m_bStopSendExtra = FALSE; // reset
|
||||
m_bStopExtraSent = FALSE;
|
||||
if (ptStop)
|
||||
{
|
||||
if (*ptStop == MAX_TIME)
|
||||
{
|
||||
DbgLog((LOG_TRACE,2,TEXT("StopAt: Cancel stop")));
|
||||
CancelStop();
|
||||
// If there's now a command to start in the future, we assume
|
||||
// they want to be stopped when the graph is first run
|
||||
if (m_FilterState == State_Stopped && m_tStartTime < MAX_TIME) {
|
||||
m_StreamState = STREAM_DISCARDING;
|
||||
DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
DbgLog((LOG_TRACE,2,TEXT("StopAt: %dms extra=%d"),
|
||||
(int)(*ptStop/10000), bSendExtra));
|
||||
// if the first command is to stop in the future, then we assume they
|
||||
// want to be started when the graph is first run
|
||||
if (m_FilterState == State_Stopped && m_tStartTime > *ptStop) {
|
||||
m_StreamState = STREAM_FLOWING;
|
||||
DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
|
||||
}
|
||||
m_bStopSendExtra = bSendExtra;
|
||||
m_tStopTime = *ptStop;
|
||||
m_dwStopCookie = dwCookie;
|
||||
m_StreamStateOnStop = STREAM_DISCARDING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DbgLog((LOG_TRACE,2,TEXT("StopAt: now")));
|
||||
// sending an extra frame when told to stop now would mess people up
|
||||
m_bStopSendExtra = FALSE;
|
||||
m_tStopTime = MAX_TIME;
|
||||
m_dwStopCookie = 0;
|
||||
m_StreamState = STREAM_DISCARDING;
|
||||
m_StreamStateOnStop = STREAM_FLOWING; // no pending stop
|
||||
}
|
||||
// we might change our mind what to do with a sample we're blocking
|
||||
m_StreamEvent.Set();
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP CBaseStreamControl::StartAt
|
||||
( const REFERENCE_TIME *ptStart, DWORD dwCookie )
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
if (ptStart)
|
||||
{
|
||||
if (*ptStart == MAX_TIME)
|
||||
{
|
||||
DbgLog((LOG_TRACE,2,TEXT("StartAt: Cancel start")));
|
||||
CancelStart();
|
||||
// If there's now a command to stop in the future, we assume
|
||||
// they want to be started when the graph is first run
|
||||
if (m_FilterState == State_Stopped && m_tStopTime < MAX_TIME) {
|
||||
DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
|
||||
m_StreamState = STREAM_FLOWING;
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
DbgLog((LOG_TRACE,2,TEXT("StartAt: %dms"), (int)(*ptStart/10000)));
|
||||
// if the first command is to start in the future, then we assume they
|
||||
// want to be stopped when the graph is first run
|
||||
if (m_FilterState == State_Stopped && m_tStopTime >= *ptStart) {
|
||||
DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
|
||||
m_StreamState = STREAM_DISCARDING;
|
||||
}
|
||||
m_tStartTime = *ptStart;
|
||||
m_dwStartCookie = dwCookie;
|
||||
// if (m_tStopTime == m_tStartTime) CancelStop();
|
||||
}
|
||||
else
|
||||
{
|
||||
DbgLog((LOG_TRACE,2,TEXT("StartAt: now")));
|
||||
m_tStartTime = MAX_TIME;
|
||||
m_dwStartCookie = 0;
|
||||
m_StreamState = STREAM_FLOWING;
|
||||
}
|
||||
// we might change our mind what to do with a sample we're blocking
|
||||
m_StreamEvent.Set();
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
// Retrieve information about current settings
|
||||
STDMETHODIMP CBaseStreamControl::GetInfo(AM_STREAM_INFO *pInfo)
|
||||
{
|
||||
if (pInfo == NULL)
|
||||
return E_POINTER;
|
||||
|
||||
pInfo->tStart = m_tStartTime;
|
||||
pInfo->tStop = m_tStopTime;
|
||||
pInfo->dwStartCookie = m_dwStartCookie;
|
||||
pInfo->dwStopCookie = m_dwStopCookie;
|
||||
pInfo->dwFlags = m_bStopSendExtra ? AM_STREAM_INFO_STOP_SEND_EXTRA : 0;
|
||||
pInfo->dwFlags |= m_tStartTime == MAX_TIME ? 0 : AM_STREAM_INFO_START_DEFINED;
|
||||
pInfo->dwFlags |= m_tStopTime == MAX_TIME ? 0 : AM_STREAM_INFO_STOP_DEFINED;
|
||||
switch (m_StreamState) {
|
||||
default:
|
||||
DbgBreak("Invalid stream state");
|
||||
case STREAM_FLOWING:
|
||||
break;
|
||||
case STREAM_DISCARDING:
|
||||
pInfo->dwFlags |= AM_STREAM_INFO_DISCARDING;
|
||||
break;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
void CBaseStreamControl::ExecuteStop()
|
||||
{
|
||||
ASSERT(CritCheckIn(&m_CritSec));
|
||||
m_StreamState = m_StreamStateOnStop;
|
||||
if (m_dwStopCookie && m_pSink) {
|
||||
DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STOPPED (%d)"),
|
||||
m_dwStopCookie));
|
||||
m_pSink->Notify(EC_STREAM_CONTROL_STOPPED, (LONG_PTR)this, m_dwStopCookie);
|
||||
}
|
||||
CancelStop(); // This will do the tidy up
|
||||
}
|
||||
|
||||
void CBaseStreamControl::ExecuteStart()
|
||||
{
|
||||
ASSERT(CritCheckIn(&m_CritSec));
|
||||
m_StreamState = STREAM_FLOWING;
|
||||
if (m_dwStartCookie) {
|
||||
DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STARTED (%d)"),
|
||||
m_dwStartCookie));
|
||||
m_pSink->Notify(EC_STREAM_CONTROL_STARTED, (LONG_PTR)this, m_dwStartCookie);
|
||||
}
|
||||
CancelStart(); // This will do the tidy up
|
||||
}
|
||||
|
||||
void CBaseStreamControl::CancelStop()
|
||||
{
|
||||
ASSERT(CritCheckIn(&m_CritSec));
|
||||
m_tStopTime = MAX_TIME;
|
||||
m_dwStopCookie = 0;
|
||||
m_StreamStateOnStop = STREAM_FLOWING;
|
||||
}
|
||||
|
||||
void CBaseStreamControl::CancelStart()
|
||||
{
|
||||
ASSERT(CritCheckIn(&m_CritSec));
|
||||
m_tStartTime = MAX_TIME;
|
||||
m_dwStartCookie = 0;
|
||||
}
|
||||
|
||||
|
||||
// This guy will return one of the three StreamControlState's. Here's what the caller
|
||||
// should do for each one:
|
||||
//
|
||||
// STREAM_FLOWING: Proceed as usual (render or pass the sample on)
|
||||
// STREAM_DISCARDING: Calculate the time 'til *pSampleStart and wait that long
|
||||
// for the event handle (GetStreamEventHandle()). If the
|
||||
// wait expires, throw the sample away. If the event
|
||||
// fires, call me back, I've changed my mind.
|
||||
// I use pSampleStart (not Stop) so that live sources don't
|
||||
// block for the duration of their samples, since the clock
|
||||
// will always read approximately pSampleStart when called
|
||||
|
||||
|
||||
// All through this code, you'll notice the following rules:
|
||||
// - When start and stop time are the same, it's as if start was first
|
||||
// - An event is considered inside the sample when it's >= sample start time
|
||||
// but < sample stop time
|
||||
// - if any part of the sample is supposed to be sent, we'll send the whole
|
||||
// thing since we don't break it into smaller pieces
|
||||
// - If we skip over a start or stop without doing it, we still signal the event
|
||||
// and reset ourselves in case somebody's waiting for the event, and to make
|
||||
// sure we notice that the event is past and should be forgotten
|
||||
// Here are the 19 cases that have to be handled (x=start o=stop <-->=sample):
|
||||
//
|
||||
// 1. xo<--> start then stop
|
||||
// 2. ox<--> stop then start
|
||||
// 3. x<o-> start
|
||||
// 4. o<x-> stop then start
|
||||
// 5. x<-->o start
|
||||
// 6. o<-->x stop
|
||||
// 7. <x->o start
|
||||
// 8. <o->x no change
|
||||
// 9. <xo> start
|
||||
// 10. <ox> stop then start
|
||||
// 11. <-->xo no change
|
||||
// 12. <-->ox no change
|
||||
// 13. x<--> start
|
||||
// 14. <x-> start
|
||||
// 15. <-->x no change
|
||||
// 16. o<--> stop
|
||||
// 17. <o-> no change
|
||||
// 18. <-->o no change
|
||||
// 19. <--> no change
|
||||
|
||||
|
||||
enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckSampleTimes
|
||||
( const REFERENCE_TIME * pSampleStart, const REFERENCE_TIME * pSampleStop )
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
|
||||
ASSERT(!m_bIsFlushing);
|
||||
ASSERT(pSampleStart && pSampleStop);
|
||||
|
||||
// Don't ask me how I came up with the code below to handle all 19 cases
|
||||
// - DannyMi
|
||||
|
||||
if (m_tStopTime >= *pSampleStart)
|
||||
{
|
||||
if (m_tStartTime >= *pSampleStop)
|
||||
return m_StreamState; // cases 8 11 12 15 17 18 19
|
||||
if (m_tStopTime < m_tStartTime)
|
||||
ExecuteStop(); // case 10
|
||||
ExecuteStart(); // cases 3 5 7 9 13 14
|
||||
return m_StreamState;
|
||||
}
|
||||
|
||||
if (m_tStartTime >= *pSampleStop)
|
||||
{
|
||||
ExecuteStop(); // cases 6 16
|
||||
return m_StreamState;
|
||||
}
|
||||
|
||||
if (m_tStartTime <= m_tStopTime)
|
||||
{
|
||||
ExecuteStart();
|
||||
ExecuteStop();
|
||||
return m_StreamState; // case 1
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteStop();
|
||||
ExecuteStart();
|
||||
return m_StreamState; // cases 2 4
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckStreamState( IMediaSample * pSample )
|
||||
{
|
||||
|
||||
REFERENCE_TIME rtBufferStart, rtBufferStop;
|
||||
const BOOL bNoBufferTimes =
|
||||
pSample == NULL ||
|
||||
FAILED(pSample->GetTime(&rtBufferStart, &rtBufferStop));
|
||||
|
||||
StreamControlState state;
|
||||
LONG lWait;
|
||||
|
||||
do
|
||||
{
|
||||
// something has to break out of the blocking
|
||||
if (m_bIsFlushing || m_FilterState == State_Stopped)
|
||||
return STREAM_DISCARDING;
|
||||
|
||||
if (bNoBufferTimes) {
|
||||
// Can't do anything until we get a time stamp
|
||||
state = m_StreamState;
|
||||
break;
|
||||
} else {
|
||||
state = CheckSampleTimes( &rtBufferStart, &rtBufferStop );
|
||||
if (state == STREAM_FLOWING)
|
||||
break;
|
||||
|
||||
// we aren't supposed to send this, but we've been
|
||||
// told to send one more than we were supposed to
|
||||
// (and the stop isn't still pending and we're streaming)
|
||||
if (m_bStopSendExtra && !m_bStopExtraSent &&
|
||||
m_tStopTime == MAX_TIME &&
|
||||
m_FilterState != State_Stopped) {
|
||||
m_bStopExtraSent = TRUE;
|
||||
DbgLog((LOG_TRACE,2,TEXT("%d sending an EXTRA frame"),
|
||||
m_dwStopCookie));
|
||||
state = STREAM_FLOWING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We're in discarding mode
|
||||
|
||||
// If we've no clock, discard as fast as we can
|
||||
if (!m_pRefClock) {
|
||||
break;
|
||||
|
||||
// If we're paused, we can't discard in a timely manner because
|
||||
// there's no such thing as stream times. We must block until
|
||||
// we run or stop, or we'll end up throwing the whole stream away
|
||||
// as quickly as possible
|
||||
} else if (m_FilterState == State_Paused) {
|
||||
lWait = INFINITE;
|
||||
|
||||
} else {
|
||||
// wait until it's time for the sample until we say "discard"
|
||||
// ("discard in a timely fashion")
|
||||
REFERENCE_TIME rtNow;
|
||||
EXECUTE_ASSERT(SUCCEEDED(m_pRefClock->GetTime(&rtNow)));
|
||||
rtNow -= m_tRunStart; // Into relative ref-time
|
||||
lWait = LONG((rtBufferStart - rtNow)/10000); // 100ns -> ms
|
||||
if (lWait < 10) break; // Not worth waiting - discard early
|
||||
}
|
||||
|
||||
} while(WaitForSingleObject(GetStreamEventHandle(), lWait) != WAIT_TIMEOUT);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
void CBaseStreamControl::NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart )
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
|
||||
// or we will get confused
|
||||
if (m_FilterState == new_state)
|
||||
return;
|
||||
|
||||
switch (new_state)
|
||||
{
|
||||
case State_Stopped:
|
||||
|
||||
DbgLog((LOG_TRACE,2,TEXT("Filter is STOPPED")));
|
||||
|
||||
// execute any pending starts and stops in the right order,
|
||||
// to make sure all notifications get sent, and we end up
|
||||
// in the right state to begin next time (??? why not?)
|
||||
|
||||
if (m_tStartTime != MAX_TIME && m_tStopTime == MAX_TIME) {
|
||||
ExecuteStart();
|
||||
} else if (m_tStopTime != MAX_TIME && m_tStartTime == MAX_TIME) {
|
||||
ExecuteStop();
|
||||
} else if (m_tStopTime != MAX_TIME && m_tStartTime != MAX_TIME) {
|
||||
if (m_tStartTime <= m_tStopTime) {
|
||||
ExecuteStart();
|
||||
ExecuteStop();
|
||||
} else {
|
||||
ExecuteStop();
|
||||
ExecuteStart();
|
||||
}
|
||||
}
|
||||
// always start off flowing when the graph starts streaming
|
||||
// unless told otherwise
|
||||
m_StreamState = STREAM_FLOWING;
|
||||
m_FilterState = new_state;
|
||||
break;
|
||||
|
||||
case State_Running:
|
||||
|
||||
DbgLog((LOG_TRACE,2,TEXT("Filter is RUNNING")));
|
||||
|
||||
m_tRunStart = tStart;
|
||||
// fall-through
|
||||
|
||||
default: // case State_Paused:
|
||||
m_FilterState = new_state;
|
||||
}
|
||||
// unblock!
|
||||
m_StreamEvent.Set();
|
||||
}
|
||||
|
||||
|
||||
void CBaseStreamControl::Flushing(BOOL bInProgress)
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
m_bIsFlushing = bInProgress;
|
||||
m_StreamEvent.Set();
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: StrmCtl.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __strmctl_h__
|
||||
#define __strmctl_h__
|
||||
|
||||
class CBaseStreamControl : public IAMStreamControl
|
||||
{
|
||||
public:
|
||||
// Used by the implementation
|
||||
enum StreamControlState
|
||||
{ STREAM_FLOWING = 0x1000,
|
||||
STREAM_DISCARDING
|
||||
};
|
||||
|
||||
private:
|
||||
enum StreamControlState m_StreamState; // Current stream state
|
||||
enum StreamControlState m_StreamStateOnStop; // State after next stop
|
||||
// (i.e.Blocking or Discarding)
|
||||
|
||||
REFERENCE_TIME m_tStartTime; // MAX_TIME implies none
|
||||
REFERENCE_TIME m_tStopTime; // MAX_TIME implies none
|
||||
DWORD m_dwStartCookie; // Cookie for notification to app
|
||||
DWORD m_dwStopCookie; // Cookie for notification to app
|
||||
volatile BOOL m_bIsFlushing; // No optimization pls!
|
||||
volatile BOOL m_bStopSendExtra; // bSendExtra was set
|
||||
volatile BOOL m_bStopExtraSent; // the extra one was sent
|
||||
|
||||
CCritSec m_CritSec; // CritSec to guard above attributes
|
||||
|
||||
// Event to fire when we can come
|
||||
// out of blocking, or to come out of waiting
|
||||
// to discard if we change our minds.
|
||||
//
|
||||
CAMEvent m_StreamEvent;
|
||||
|
||||
// All of these methods execute immediately. Helpers for others.
|
||||
//
|
||||
void ExecuteStop();
|
||||
void ExecuteStart();
|
||||
void CancelStop();
|
||||
void CancelStart();
|
||||
|
||||
// Some things we need to be told by our owning filter
|
||||
// Your pin must also expose IAMStreamControl when QI'd for it!
|
||||
//
|
||||
IReferenceClock * m_pRefClock; // Need it to set advises
|
||||
// Filter must tell us via
|
||||
// SetSyncSource
|
||||
IMediaEventSink * m_pSink; // Event sink
|
||||
// Filter must tell us after it
|
||||
// creates it in JoinFilterGraph()
|
||||
FILTER_STATE m_FilterState; // Just need it!
|
||||
// Filter must tell us via
|
||||
// NotifyFilterState
|
||||
REFERENCE_TIME m_tRunStart; // Per the Run call to the filter
|
||||
|
||||
// This guy will return one of the three StreamControlState's. Here's what
|
||||
// the caller should do for each one:
|
||||
//
|
||||
// STREAM_FLOWING: Proceed as usual (render or pass the sample on)
|
||||
// STREAM_DISCARDING: Calculate the time 'til *pSampleStop and wait
|
||||
// that long for the event handle
|
||||
// (GetStreamEventHandle()). If the wait
|
||||
// expires, throw the sample away. If the event
|
||||
// fires, call me back - I've changed my mind.
|
||||
//
|
||||
enum StreamControlState CheckSampleTimes( const REFERENCE_TIME * pSampleStart,
|
||||
const REFERENCE_TIME * pSampleStop );
|
||||
|
||||
public:
|
||||
// You don't have to tell us much when we're created, but there are other
|
||||
// obligations that must be met. See SetSyncSource & NotifyFilterState
|
||||
// below.
|
||||
//
|
||||
CBaseStreamControl();
|
||||
~CBaseStreamControl();
|
||||
|
||||
// If you want this class to work properly, there are thing you need to
|
||||
// (keep) telling it. Filters with pins that use this class
|
||||
// should ensure that they pass through to this method any calls they
|
||||
// receive on their SetSyncSource.
|
||||
|
||||
// We need a clock to see what time it is. This is for the
|
||||
// "discard in a timely fashion" logic. If we discard everything as
|
||||
// quick as possible, a whole 60 minute file could get discarded in the
|
||||
// first 10 seconds, and if somebody wants to turn streaming on at 30
|
||||
// minutes into the file, and they make the call more than a few seconds
|
||||
// after the graph is run, it may be too late!
|
||||
// So we hold every sample until it's time has gone, then we discard it.
|
||||
// The filter should call this when it gets a SetSyncSource
|
||||
//
|
||||
void SetSyncSource( IReferenceClock * pRefClock )
|
||||
{
|
||||
CAutoLock lck(&m_CritSec);
|
||||
if (m_pRefClock) m_pRefClock->Release();
|
||||
m_pRefClock = pRefClock;
|
||||
if (m_pRefClock) m_pRefClock->AddRef();
|
||||
}
|
||||
|
||||
// Set event sink for notifications
|
||||
// The filter should call this in its JoinFilterGraph after it creates the
|
||||
// IMediaEventSink
|
||||
//
|
||||
void SetFilterGraph( IMediaEventSink *pSink ) {
|
||||
m_pSink = pSink;
|
||||
}
|
||||
|
||||
// Since we schedule in stream time, we need the tStart and must track the
|
||||
// state of our owning filter.
|
||||
// The app should call this ever state change
|
||||
//
|
||||
void NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart = 0 );
|
||||
|
||||
// Filter should call Flushing(TRUE) in BeginFlush,
|
||||
// and Flushing(FALSE) in EndFlush.
|
||||
//
|
||||
void Flushing( BOOL bInProgress );
|
||||
|
||||
|
||||
// The two main methods of IAMStreamControl
|
||||
|
||||
// Class adds default values suitable for immediate
|
||||
// muting and unmuting of the stream.
|
||||
|
||||
STDMETHODIMP StopAt( const REFERENCE_TIME * ptStop = NULL,
|
||||
BOOL bSendExtra = FALSE,
|
||||
DWORD dwCookie = 0 );
|
||||
STDMETHODIMP StartAt( const REFERENCE_TIME * ptStart = NULL,
|
||||
DWORD dwCookie = 0 );
|
||||
STDMETHODIMP GetInfo( AM_STREAM_INFO *pInfo);
|
||||
|
||||
// Helper function for pin's receive method. Call this with
|
||||
// the sample and we'll tell you what to do with it. We'll do a
|
||||
// WaitForSingleObject within this call if one is required. This is
|
||||
// a "What should I do with this sample?" kind of call. We'll tell the
|
||||
// caller to either flow it or discard it.
|
||||
// If pSample is NULL we evaluate based on the current state
|
||||
// settings
|
||||
enum StreamControlState CheckStreamState( IMediaSample * pSample );
|
||||
|
||||
private:
|
||||
// These don't require locking, but we are relying on the fact that
|
||||
// m_StreamState can be retrieved with integrity, and is a snap shot that
|
||||
// may have just been, or may be just about to be, changed.
|
||||
HANDLE GetStreamEventHandle() const { return m_StreamEvent; }
|
||||
enum StreamControlState GetStreamState() const { return m_StreamState; }
|
||||
BOOL IsStreaming() const { return m_StreamState == STREAM_FLOWING; }
|
||||
};
|
||||
|
||||
#endif
|
@ -1,74 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: SysClock.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements a system clock based on
|
||||
// IReferenceClock.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include "streams.h"
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
#ifdef FILTER_DLL
|
||||
|
||||
/* List of class IDs and creator functions for the class factory. This
|
||||
provides the link between the OLE entry point in the DLL and an object
|
||||
being created. The class factory will call the static CreateInstance
|
||||
function when it is asked to create a CLSID_SystemClock object */
|
||||
|
||||
CFactoryTemplate g_Templates[1] = {
|
||||
{&CLSID_SystemClock, CSystemClock::CreateInstance}
|
||||
};
|
||||
|
||||
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
|
||||
#endif
|
||||
|
||||
/* This goes in the factory template table to create new instances */
|
||||
CUnknown * WINAPI CSystemClock::CreateInstance(LPUNKNOWN pUnk,HRESULT *phr)
|
||||
{
|
||||
return new CSystemClock(NAME("System reference clock"),pUnk, phr);
|
||||
}
|
||||
|
||||
|
||||
CSystemClock::CSystemClock(TCHAR *pName,LPUNKNOWN pUnk,HRESULT *phr) :
|
||||
CBaseReferenceClock(pName, pUnk, phr)
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP CSystemClock::NonDelegatingQueryInterface(
|
||||
REFIID riid,
|
||||
void ** ppv)
|
||||
{
|
||||
if (riid == IID_IPersist)
|
||||
{
|
||||
return GetInterface(static_cast<IPersist *>(this), ppv);
|
||||
}
|
||||
else if (riid == IID_IAMClockAdjust)
|
||||
{
|
||||
return GetInterface(static_cast<IAMClockAdjust *>(this), ppv);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CBaseReferenceClock::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the clock's clsid */
|
||||
STDMETHODIMP
|
||||
CSystemClock::GetClassID(CLSID *pClsID)
|
||||
{
|
||||
CheckPointer(pClsID,E_POINTER);
|
||||
ValidateReadWritePtr(pClsID,sizeof(CLSID));
|
||||
*pClsID = CLSID_SystemClock;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP
|
||||
CSystemClock::SetClockDelta(REFERENCE_TIME rtDelta)
|
||||
{
|
||||
return SetTimeDelta(rtDelta);
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: SysClock.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a system clock implementation of
|
||||
// IReferenceClock.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __SYSTEMCLOCK__
|
||||
#define __SYSTEMCLOCK__
|
||||
|
||||
//
|
||||
// Base clock. Uses timeGetTime ONLY
|
||||
// Uses most of the code in the base reference clock.
|
||||
// Provides GetTime
|
||||
//
|
||||
|
||||
class CSystemClock : public CBaseReferenceClock, public IAMClockAdjust, public IPersist
|
||||
{
|
||||
public:
|
||||
// We must be able to create an instance of ourselves
|
||||
static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
|
||||
CSystemClock(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void ** ppv);
|
||||
|
||||
// Yield up our class id so that we can be persisted
|
||||
// Implement required Ipersist method
|
||||
STDMETHODIMP GetClassID(CLSID *pClsID);
|
||||
|
||||
// IAMClockAdjust methods
|
||||
STDMETHODIMP SetClockDelta(REFERENCE_TIME rtDelta);
|
||||
}; //CSystemClock
|
||||
|
||||
#endif /* __SYSTEMCLOCK__ */
|
File diff suppressed because it is too large
Load Diff
@ -1,304 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Transfrm.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines classes from which simple
|
||||
// transform codecs may be derived.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// It assumes the codec has one input and one output stream, and has no
|
||||
// interest in memory management, interface negotiation or anything else.
|
||||
//
|
||||
// derive your class from this, and supply Transform and the media type/format
|
||||
// negotiation functions. Implement that class, compile and link and
|
||||
// you're done.
|
||||
|
||||
|
||||
#ifndef __TRANSFRM__
|
||||
#define __TRANSFRM__
|
||||
|
||||
// ======================================================================
|
||||
// This is the com object that represents a simple transform filter. It
|
||||
// supports IBaseFilter, IMediaFilter and two pins through nested interfaces
|
||||
// ======================================================================
|
||||
|
||||
class CTransformFilter;
|
||||
|
||||
// ==================================================
|
||||
// Implements the input pin
|
||||
// ==================================================
|
||||
|
||||
class CTransformInputPin : public CBaseInputPin
|
||||
{
|
||||
friend class CTransformFilter;
|
||||
|
||||
protected:
|
||||
CTransformFilter *m_pTransformFilter;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
CTransformInputPin(
|
||||
TCHAR *pObjectName,
|
||||
CTransformFilter *pTransformFilter,
|
||||
HRESULT * phr,
|
||||
LPCWSTR pName);
|
||||
#ifdef UNICODE
|
||||
CTransformInputPin(
|
||||
char *pObjectName,
|
||||
CTransformFilter *pTransformFilter,
|
||||
HRESULT * phr,
|
||||
LPCWSTR pName);
|
||||
#endif
|
||||
|
||||
STDMETHODIMP QueryId(LPWSTR * Id)
|
||||
{
|
||||
return AMGetWideString(L"In", Id);
|
||||
}
|
||||
|
||||
// Grab and release extra interfaces if required
|
||||
|
||||
HRESULT CheckConnect(IPin *pPin);
|
||||
HRESULT BreakConnect();
|
||||
HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
|
||||
// check that we can support this output type
|
||||
HRESULT CheckMediaType(const CMediaType* mtIn);
|
||||
|
||||
// set the connection media type
|
||||
HRESULT SetMediaType(const CMediaType* mt);
|
||||
|
||||
// --- IMemInputPin -----
|
||||
|
||||
// here's the next block of data from the stream.
|
||||
// AddRef it yourself if you need to hold it beyond the end
|
||||
// of this call.
|
||||
STDMETHODIMP Receive(IMediaSample * pSample);
|
||||
|
||||
// provide EndOfStream that passes straight downstream
|
||||
// (there is no queued data)
|
||||
STDMETHODIMP EndOfStream(void);
|
||||
|
||||
// passes it to CTransformFilter::BeginFlush
|
||||
STDMETHODIMP BeginFlush(void);
|
||||
|
||||
// passes it to CTransformFilter::EndFlush
|
||||
STDMETHODIMP EndFlush(void);
|
||||
|
||||
STDMETHODIMP NewSegment(
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop,
|
||||
double dRate);
|
||||
|
||||
// Check if it's OK to process samples
|
||||
virtual HRESULT CheckStreaming();
|
||||
|
||||
// Media type
|
||||
public:
|
||||
CMediaType& CurrentMediaType() { return m_mt; };
|
||||
|
||||
};
|
||||
|
||||
// ==================================================
|
||||
// Implements the output pin
|
||||
// ==================================================
|
||||
|
||||
class CTransformOutputPin : public CBaseOutputPin
|
||||
{
|
||||
friend class CTransformFilter;
|
||||
|
||||
protected:
|
||||
CTransformFilter *m_pTransformFilter;
|
||||
|
||||
public:
|
||||
|
||||
// implement IMediaPosition by passing upstream
|
||||
IUnknown * m_pPosition;
|
||||
|
||||
CTransformOutputPin(
|
||||
TCHAR *pObjectName,
|
||||
CTransformFilter *pTransformFilter,
|
||||
HRESULT * phr,
|
||||
LPCWSTR pName);
|
||||
#ifdef UNICODE
|
||||
CTransformOutputPin(
|
||||
CHAR *pObjectName,
|
||||
CTransformFilter *pTransformFilter,
|
||||
HRESULT * phr,
|
||||
LPCWSTR pName);
|
||||
#endif
|
||||
~CTransformOutputPin();
|
||||
|
||||
// override to expose IMediaPosition
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
|
||||
|
||||
// --- CBaseOutputPin ------------
|
||||
|
||||
STDMETHODIMP QueryId(LPWSTR * Id)
|
||||
{
|
||||
return AMGetWideString(L"Out", Id);
|
||||
}
|
||||
|
||||
// Grab and release extra interfaces if required
|
||||
|
||||
HRESULT CheckConnect(IPin *pPin);
|
||||
HRESULT BreakConnect();
|
||||
HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
|
||||
// check that we can support this output type
|
||||
HRESULT CheckMediaType(const CMediaType* mtOut);
|
||||
|
||||
// set the connection media type
|
||||
HRESULT SetMediaType(const CMediaType *pmt);
|
||||
|
||||
// called from CBaseOutputPin during connection to ask for
|
||||
// the count and size of buffers we need.
|
||||
HRESULT DecideBufferSize(
|
||||
IMemAllocator * pAlloc,
|
||||
ALLOCATOR_PROPERTIES *pProp);
|
||||
|
||||
// returns the preferred formats for a pin
|
||||
HRESULT GetMediaType(int iPosition,CMediaType *pMediaType);
|
||||
|
||||
// inherited from IQualityControl via CBasePin
|
||||
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
|
||||
|
||||
// Media type
|
||||
public:
|
||||
CMediaType& CurrentMediaType() { return m_mt; };
|
||||
};
|
||||
|
||||
|
||||
class AM_NOVTABLE CTransformFilter : public CBaseFilter
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// map getpin/getpincount for base enum of pins to owner
|
||||
// override this to return more specialised pin objects
|
||||
|
||||
virtual int GetPinCount();
|
||||
virtual CBasePin * GetPin(int n);
|
||||
STDMETHODIMP FindPin(LPCWSTR Id, IPin **ppPin);
|
||||
|
||||
// override state changes to allow derived transform filter
|
||||
// to control streaming start/stop
|
||||
STDMETHODIMP Stop();
|
||||
STDMETHODIMP Pause();
|
||||
|
||||
public:
|
||||
|
||||
CTransformFilter(TCHAR *, LPUNKNOWN, REFCLSID clsid);
|
||||
#ifdef UNICODE
|
||||
CTransformFilter(CHAR *, LPUNKNOWN, REFCLSID clsid);
|
||||
#endif
|
||||
~CTransformFilter();
|
||||
|
||||
// =================================================================
|
||||
// ----- override these bits ---------------------------------------
|
||||
// =================================================================
|
||||
|
||||
// These must be supplied in a derived class
|
||||
|
||||
virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);
|
||||
|
||||
// check if you can support mtIn
|
||||
virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;
|
||||
|
||||
// check if you can support the transform from this input to this output
|
||||
virtual HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut) PURE;
|
||||
|
||||
// this goes in the factory template table to create new instances
|
||||
// static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
|
||||
|
||||
// call the SetProperties function with appropriate arguments
|
||||
virtual HRESULT DecideBufferSize(
|
||||
IMemAllocator * pAllocator,
|
||||
ALLOCATOR_PROPERTIES *pprop) PURE;
|
||||
|
||||
// override to suggest OUTPUT pin media types
|
||||
virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE;
|
||||
|
||||
|
||||
|
||||
// =================================================================
|
||||
// ----- Optional Override Methods -----------------------
|
||||
// =================================================================
|
||||
|
||||
// you can also override these if you want to know about streaming
|
||||
virtual HRESULT StartStreaming();
|
||||
virtual HRESULT StopStreaming();
|
||||
|
||||
// override if you can do anything constructive with quality notifications
|
||||
virtual HRESULT AlterQuality(Quality q);
|
||||
|
||||
// override this to know when the media type is actually set
|
||||
virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);
|
||||
|
||||
// chance to grab extra interfaces on connection
|
||||
virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);
|
||||
virtual HRESULT BreakConnect(PIN_DIRECTION dir);
|
||||
virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);
|
||||
|
||||
// chance to customize the transform process
|
||||
virtual HRESULT Receive(IMediaSample *pSample);
|
||||
|
||||
// Standard setup for output sample
|
||||
HRESULT InitializeOutputSample(IMediaSample *pSample, IMediaSample **ppOutSample);
|
||||
|
||||
// if you override Receive, you may need to override these three too
|
||||
virtual HRESULT EndOfStream(void);
|
||||
virtual HRESULT BeginFlush(void);
|
||||
virtual HRESULT EndFlush(void);
|
||||
virtual HRESULT NewSegment(
|
||||
REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop,
|
||||
double dRate);
|
||||
|
||||
#ifdef PERF
|
||||
// Override to register performance measurement with a less generic string
|
||||
// You should do this to avoid confusion with other filters
|
||||
virtual void RegisterPerfId()
|
||||
{m_idTransform = MSR_REGISTER(TEXT("Transform"));}
|
||||
#endif // PERF
|
||||
|
||||
|
||||
// implementation details
|
||||
|
||||
protected:
|
||||
|
||||
#ifdef PERF
|
||||
int m_idTransform; // performance measuring id
|
||||
#endif
|
||||
BOOL m_bEOSDelivered; // have we sent EndOfStream
|
||||
BOOL m_bSampleSkipped; // Did we just skip a frame
|
||||
BOOL m_bQualityChanged; // Have we degraded?
|
||||
|
||||
// critical section protecting filter state.
|
||||
|
||||
CCritSec m_csFilter;
|
||||
|
||||
// critical section stopping state changes (ie Stop) while we're
|
||||
// processing a sample.
|
||||
//
|
||||
// This critical section is held when processing
|
||||
// events that occur on the receive thread - Receive() and EndOfStream().
|
||||
//
|
||||
// If you want to hold both m_csReceive and m_csFilter then grab
|
||||
// m_csFilter FIRST - like CTransformFilter::Stop() does.
|
||||
|
||||
CCritSec m_csReceive;
|
||||
|
||||
// these hold our input and output pins
|
||||
|
||||
friend class CTransformInputPin;
|
||||
friend class CTransformOutputPin;
|
||||
CTransformInputPin *m_pInput;
|
||||
CTransformOutputPin *m_pOutput;
|
||||
};
|
||||
|
||||
#endif /* __TRANSFRM__ */
|
||||
|
||||
|
@ -1,966 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: TransIP.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements class for simple Transform-
|
||||
// In-Place filters such as audio.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// How allocators are decided.
|
||||
//
|
||||
// An in-place transform tries to do its work in someone else's buffers.
|
||||
// It tries to persuade the filters on either side to use the same allocator
|
||||
// (and for that matter the same media type). In desperation, if the downstream
|
||||
// filter refuses to supply an allocator and the upstream filter offers only
|
||||
// a read-only one then it will provide an allocator.
|
||||
// if the upstream filter insists on a read-only allocator then the transform
|
||||
// filter will (reluctantly) copy the data before transforming it.
|
||||
//
|
||||
// In order to pass an allocator through it needs to remember the one it got
|
||||
// from the first connection to pass it on to the second one.
|
||||
//
|
||||
// It is good if we can avoid insisting on a particular order of connection
|
||||
// (There is a precedent for insisting on the input
|
||||
// being connected first. Insisting on the output being connected first is
|
||||
// not allowed. That would break RenderFile.)
|
||||
//
|
||||
// The base pin classes (CBaseOutputPin and CBaseInputPin) both have a
|
||||
// m_pAllocator member which is used in places like
|
||||
// CBaseOutputPin::GetDeliveryBuffer and CBaseInputPin::Inactive.
|
||||
// To avoid lots of extra overriding, we should keep these happy
|
||||
// by using these pointers.
|
||||
//
|
||||
// When each pin is connected, it will set the corresponding m_pAllocator
|
||||
// and will have a single ref-count on that allocator.
|
||||
//
|
||||
// Refcounts are acquired by GetAllocator calls which return AddReffed
|
||||
// allocators and are released in one of:
|
||||
// CBaseInputPin::Disconnect
|
||||
// CBaseOutputPin::BreakConect
|
||||
// In each case m_pAllocator is set to NULL after the release, so this
|
||||
// is the last chance to ever release it. If there should ever be
|
||||
// multiple refcounts associated with the same pointer, this had better
|
||||
// be cleared up before that happens. To avoid such problems, we'll
|
||||
// stick with one per pointer.
|
||||
|
||||
|
||||
|
||||
// RECONNECTING and STATE CHANGES
|
||||
//
|
||||
// Each pin could be disconnected, connected with a read-only allocator,
|
||||
// connected with an upstream read/write allocator, connected with an
|
||||
// allocator from downstream or connected with its own allocator.
|
||||
// Five states for each pin gives a data space of 25 states.
|
||||
//
|
||||
// Notation:
|
||||
//
|
||||
// R/W == read/write
|
||||
// R-O == read-only
|
||||
//
|
||||
// <input pin state> <output pin state> <comments>
|
||||
//
|
||||
// 00 means an unconnected pin.
|
||||
// <- means using a R/W allocator from the upstream filter
|
||||
// <= means using a R-O allocator from an upstream filter
|
||||
// || means using our own (R/W) allocator.
|
||||
// -> means using a R/W allocator from a downstream filter
|
||||
// (a R-O allocator from downstream is nonsense, it can't ever work).
|
||||
//
|
||||
//
|
||||
// That makes 25 possible states. Some states are nonsense (two different
|
||||
// allocators from the same place). These are just an artifact of the notation.
|
||||
// <= <- Nonsense.
|
||||
// <- <= Nonsense
|
||||
// Some states are illegal (the output pin never accepts a R-O allocator):
|
||||
// 00 <= !! Error !!
|
||||
// <= <= !! Error !!
|
||||
// || <= !! Error !!
|
||||
// -> <= !! Error !!
|
||||
// Three states appears to be inaccessible:
|
||||
// -> || Inaccessible
|
||||
// || -> Inaccessible
|
||||
// || <- Inaccessible
|
||||
// Some states only ever occur as intermediates with a pending reconnect which
|
||||
// is guaranteed to finish in another state.
|
||||
// -> 00 ?? unstable goes to || 00
|
||||
// 00 <- ?? unstable goes to 00 ||
|
||||
// -> <- ?? unstable goes to -> ->
|
||||
// <- || ?? unstable goes to <- <-
|
||||
// <- -> ?? unstable goes to <- <-
|
||||
// And that leaves 11 possible resting states:
|
||||
// 1 00 00 Nothing connected.
|
||||
// 2 <- 00 Input pin connected.
|
||||
// 3 <= 00 Input pin connected using R-O allocator.
|
||||
// 4 || 00 Needs several state changes to get here.
|
||||
// 5 00 || Output pin connected using our allocator
|
||||
// 6 00 -> Downstream only connected
|
||||
// 7 || || Undesirable but can be forced upon us.
|
||||
// 8 <= || Copy forced. <= -> is preferable
|
||||
// 9 <= -> OK - forced to copy.
|
||||
// 10 <- <- Transform in place (ideal)
|
||||
// 11 -> -> Transform in place (ideal)
|
||||
//
|
||||
// The object of the exercise is to ensure that we finish up in states
|
||||
// 10 or 11 whenever possible. State 10 is only possible if the upstream
|
||||
// filter has a R/W allocator (the AVI splitter notoriously
|
||||
// doesn't) and state 11 is only possible if the downstream filter does
|
||||
// offer an allocator.
|
||||
//
|
||||
// The transition table (entries marked * go via a reconnect)
|
||||
//
|
||||
// There are 8 possible transitions:
|
||||
// A: Connect upstream to filter with R-O allocator that insists on using it.
|
||||
// B: Connect upstream to filter with R-O allocator but chooses not to use it.
|
||||
// C: Connect upstream to filter with R/W allocator and insists on using it.
|
||||
// D: Connect upstream to filter with R/W allocator but chooses not to use it.
|
||||
// E: Connect downstream to a filter that offers an allocator
|
||||
// F: Connect downstream to a filter that does not offer an allocator
|
||||
// G: disconnect upstream
|
||||
// H: Disconnect downstream
|
||||
//
|
||||
// A B C D E F G H
|
||||
// ---------------------------------------------------------
|
||||
// 00 00 1 | 3 3 2 2 6 5 . . |1 00 00
|
||||
// <- 00 2 | . . . . *10/11 10 1 . |2 <- 00
|
||||
// <= 00 3 | . . . . *9/11 *7/8 1 . |3 <= 00
|
||||
// || 00 4 | . . . . *8 *7 1 . |4 || 00
|
||||
// 00 || 5 | 8 7 *10 7 . . . 1 |5 00 ||
|
||||
// 00 -> 6 | 9 11 *10 11 . . . 1 |6 00 ->
|
||||
// || || 7 | . . . . . . 5 4 |7 || ||
|
||||
// <= || 8 | . . . . . . 5 3 |8 <= ||
|
||||
// <= -> 9 | . . . . . . 6 3 |9 <= ->
|
||||
// <- <- 10| . . . . . . *5/6 2 |10 <- <-
|
||||
// -> -> 11| . . . . . . 6 *2/3 |11 -> ->
|
||||
// ---------------------------------------------------------
|
||||
// A B C D E F G H
|
||||
//
|
||||
// All these states are accessible without requiring any filter to
|
||||
// change its behaviour but not all transitions are accessible, for
|
||||
// instance a transition from state 4 to anywhere other than
|
||||
// state 8 requires that the upstream filter first offer a R-O allocator
|
||||
// and then changes its mind and offer R/W. This is NOT allowable - it
|
||||
// leads to things like the output pin getting a R/W allocator from
|
||||
// upstream and then the input pin being told it can only have a R-O one.
|
||||
// Note that you CAN change (say) the upstream filter for a different one, but
|
||||
// only as a disconnect / connect, not as a Reconnect. (Exercise for
|
||||
// the reader is to see how you get into state 4).
|
||||
//
|
||||
// The reconnection stuff goes as follows (some of the cases shown here as
|
||||
// "no reconnect" may get one to finalise media type - an old story).
|
||||
// If there is a reconnect where it says "no reconnect" here then the
|
||||
// reconnection must not change the allocator choice.
|
||||
//
|
||||
// state 2: <- 00 transition E <- <- case C <- <- (no change)
|
||||
// case D -> <- and then to -> ->
|
||||
//
|
||||
// state 2: <- 00 transition F <- <- (no reconnect)
|
||||
//
|
||||
// state 3: <= 00 transition E <= -> case A <= -> (no change)
|
||||
// case B -> ->
|
||||
// transition F <= || case A <= || (no change)
|
||||
// case B || ||
|
||||
//
|
||||
// state 4: || 00 transition E || || case B -> || and then all cases to -> ->
|
||||
// F || || case B || || (no change)
|
||||
//
|
||||
// state 5: 00 || transition A <= || (no reconnect)
|
||||
// B || || (no reconnect)
|
||||
// C <- || all cases <- <-
|
||||
// D || || (unfortunate, but upstream's choice)
|
||||
//
|
||||
// state 6: 00 -> transition A <= -> (no reconnect)
|
||||
// B -> -> (no reconnect)
|
||||
// C <- -> all cases <- <-
|
||||
// D -> -> (no reconnect)
|
||||
//
|
||||
// state 10:<- <- transition G 00 <- case E 00 ->
|
||||
// case F 00 ||
|
||||
//
|
||||
// state 11:-> -> transition H -> 00 case A <= 00 (schizo)
|
||||
// case B <= 00
|
||||
// case C <- 00 (schizo)
|
||||
// case D <- 00
|
||||
//
|
||||
// The Rules:
|
||||
// To sort out media types:
|
||||
// The input is reconnected
|
||||
// if the input pin is connected and the output pin connects
|
||||
// The output is reconnected
|
||||
// If the output pin is connected
|
||||
// and the input pin connects to a different media type
|
||||
//
|
||||
// To sort out allocators:
|
||||
// The input is reconnected
|
||||
// if the output disconnects and the input was using a downstream allocator
|
||||
// The output pin calls SetAllocator to pass on a new allocator
|
||||
// if the output is connected and
|
||||
// if the input disconnects and the output was using an upstream allocator
|
||||
// if the input acquires an allocator different from the output one
|
||||
// and that new allocator is not R-O
|
||||
//
|
||||
// Data is copied (i.e. call getbuffer and copy the data before transforming it)
|
||||
// if the two allocators are different.
|
||||
|
||||
|
||||
|
||||
// CHAINS of filters:
|
||||
//
|
||||
// We sit between two filters (call them A and Z). We should finish up
|
||||
// with the same allocator on both of our pins and that should be the
|
||||
// same one that A and Z would have agreed on if we hadn't been in the
|
||||
// way. Furthermore, it should not matter how many in-place transforms
|
||||
// are in the way. Let B, C, D... be in-place transforms ("us").
|
||||
// Here's how it goes:
|
||||
//
|
||||
// 1.
|
||||
// A connects to B. They agree on A's allocator.
|
||||
// A-a->B
|
||||
//
|
||||
// 2.
|
||||
// B connects to C. Same story. There is no point in a reconnect, but
|
||||
// B will request an input reconnect anyway.
|
||||
// A-a->B-a->C
|
||||
//
|
||||
// 3.
|
||||
// C connects to Z.
|
||||
// C insists on using A's allocator, but compromises by requesting a reconnect.
|
||||
// of C's input.
|
||||
// A-a->B-?->C-a->Z
|
||||
//
|
||||
// We now have pending reconnects on both A--->B and B--->C
|
||||
//
|
||||
// 4.
|
||||
// The A--->B link is reconnected.
|
||||
// A asks B for an allocator. B sees that it has a downstream connection so
|
||||
// asks its downstream input pin i.e. C's input pin for an allocator. C sees
|
||||
// that it too has a downstream connection so asks Z for an allocator.
|
||||
//
|
||||
// Even though Z's input pin is connected, it is being asked for an allocator.
|
||||
// It could refuse, in which case the chain is done and will use A's allocator
|
||||
// Alternatively, Z may supply one. A chooses either Z's or A's own one.
|
||||
// B's input pin gets NotifyAllocator called to tell it the decision and it
|
||||
// propagates this downstream by calling ReceiveAllocator on its output pin
|
||||
// which calls NotifyAllocator on the next input pin downstream etc.
|
||||
// If the choice is Z then it goes:
|
||||
// A-z->B-a->C-a->Z
|
||||
// A-z->B-z->C-a->Z
|
||||
// A-z->B-z->C-z->Z
|
||||
//
|
||||
// And that's IT!! Any further (essentially spurious) reconnects peter out
|
||||
// with no change in the chain.
|
||||
|
||||
#include "streams.h"
|
||||
#include "measure.h"
|
||||
#include "transip.h"
|
||||
|
||||
|
||||
// =================================================================
|
||||
// Implements the CTransInPlaceFilter class
|
||||
// =================================================================
|
||||
|
||||
CTransInPlaceFilter::CTransInPlaceFilter
|
||||
( TCHAR *pName,
|
||||
LPUNKNOWN pUnk,
|
||||
REFCLSID clsid,
|
||||
HRESULT *phr,
|
||||
bool bModifiesData
|
||||
)
|
||||
: CTransformFilter(pName, pUnk, clsid),
|
||||
m_bModifiesData(bModifiesData)
|
||||
{
|
||||
#ifdef PERF
|
||||
RegisterPerfId();
|
||||
#endif // PERF
|
||||
|
||||
} // constructor
|
||||
|
||||
#ifdef UNICODE
|
||||
CTransInPlaceFilter::CTransInPlaceFilter
|
||||
( CHAR *pName,
|
||||
LPUNKNOWN pUnk,
|
||||
REFCLSID clsid,
|
||||
HRESULT *phr,
|
||||
bool bModifiesData
|
||||
)
|
||||
: CTransformFilter(pName, pUnk, clsid),
|
||||
m_bModifiesData(bModifiesData)
|
||||
{
|
||||
#ifdef PERF
|
||||
RegisterPerfId();
|
||||
#endif // PERF
|
||||
|
||||
} // constructor
|
||||
#endif
|
||||
|
||||
// return a non-addrefed CBasePin * for the user to addref if he holds onto it
|
||||
// for longer than his pointer to us. We create the pins dynamically when they
|
||||
// are asked for rather than in the constructor. This is because we want to
|
||||
// give the derived class an oppportunity to return different pin objects
|
||||
|
||||
// As soon as any pin is needed we create both (this is different from the
|
||||
// usual transform filter) because enumerators, allocators etc are passed
|
||||
// through from one pin to another and it becomes very painful if the other
|
||||
// pin isn't there. If we fail to create either pin we ensure we fail both.
|
||||
|
||||
CBasePin *
|
||||
CTransInPlaceFilter::GetPin(int n)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
// Create an input pin if not already done
|
||||
|
||||
if (m_pInput == NULL) {
|
||||
|
||||
m_pInput = new CTransInPlaceInputPin( NAME("TransInPlace input pin")
|
||||
, this // Owner filter
|
||||
, &hr // Result code
|
||||
, L"Input" // Pin name
|
||||
);
|
||||
|
||||
// Constructor for CTransInPlaceInputPin can't fail
|
||||
ASSERT(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
// Create an output pin if not already done
|
||||
|
||||
if (m_pInput!=NULL && m_pOutput == NULL) {
|
||||
|
||||
m_pOutput = new CTransInPlaceOutputPin( NAME("TransInPlace output pin")
|
||||
, this // Owner filter
|
||||
, &hr // Result code
|
||||
, L"Output" // Pin name
|
||||
);
|
||||
|
||||
// a failed return code should delete the object
|
||||
|
||||
ASSERT(SUCCEEDED(hr));
|
||||
if (m_pOutput == NULL) {
|
||||
delete m_pInput;
|
||||
m_pInput = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the appropriate pin
|
||||
|
||||
ASSERT (n>=0 && n<=1);
|
||||
if (n == 0) {
|
||||
return m_pInput;
|
||||
} else if (n==1) {
|
||||
return m_pOutput;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // GetPin
|
||||
|
||||
|
||||
|
||||
// dir is the direction of our pin.
|
||||
// pReceivePin is the pin we are connecting to.
|
||||
HRESULT CTransInPlaceFilter::CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(pReceivePin);
|
||||
ASSERT(m_pInput);
|
||||
ASSERT(m_pOutput);
|
||||
|
||||
// if we are not part of a graph, then don't indirect the pointer
|
||||
// this probably prevents use of the filter without a filtergraph
|
||||
if (!m_pGraph) {
|
||||
return VFW_E_NOT_IN_GRAPH;
|
||||
}
|
||||
|
||||
// Always reconnect the input to account for buffering changes
|
||||
//
|
||||
// Because we don't get to suggest a type on ReceiveConnection
|
||||
// we need another way of making sure the right type gets used.
|
||||
//
|
||||
// One way would be to have our EnumMediaTypes return our output
|
||||
// connection type first but more deterministic and simple is to
|
||||
// call ReconnectEx passing the type we want to reconnect with
|
||||
// via the base class ReconeectPin method.
|
||||
|
||||
if (dir == PINDIR_OUTPUT) {
|
||||
if( m_pInput->IsConnected() ) {
|
||||
return ReconnectPin( m_pInput, &m_pOutput->CurrentMediaType() );
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
ASSERT(dir == PINDIR_INPUT);
|
||||
|
||||
// Reconnect output if necessary
|
||||
|
||||
if( m_pOutput->IsConnected() ) {
|
||||
|
||||
if ( m_pInput->CurrentMediaType()
|
||||
!= m_pOutput->CurrentMediaType()
|
||||
) {
|
||||
return ReconnectPin( m_pOutput, &m_pInput->CurrentMediaType() );
|
||||
}
|
||||
}
|
||||
return NOERROR;
|
||||
|
||||
} // ComnpleteConnect
|
||||
|
||||
|
||||
//
|
||||
// DecideBufferSize
|
||||
//
|
||||
// Tell the output pin's allocator what size buffers we require.
|
||||
// *pAlloc will be the allocator our output pin is using.
|
||||
//
|
||||
|
||||
HRESULT CTransInPlaceFilter::DecideBufferSize
|
||||
( IMemAllocator *pAlloc
|
||||
, ALLOCATOR_PROPERTIES *pProperties
|
||||
)
|
||||
{
|
||||
ALLOCATOR_PROPERTIES Request, Actual;
|
||||
HRESULT hr;
|
||||
|
||||
// If we are connected upstream, get his views
|
||||
if (m_pInput->IsConnected()) {
|
||||
// Get the input pin allocator, and get its size and count.
|
||||
// we don't care about his alignment and prefix.
|
||||
|
||||
hr = InputPin()->PeekAllocator()->GetProperties(&Request);
|
||||
if (FAILED(hr)) {
|
||||
// Input connected but with a secretive allocator - enough!
|
||||
return hr;
|
||||
}
|
||||
} else {
|
||||
// We're reduced to blind guessing. Let's guess one byte and if
|
||||
// this isn't enough then when the other pin does get connected
|
||||
// we can revise it.
|
||||
ZeroMemory(&Request, sizeof(Request));
|
||||
Request.cBuffers = 1;
|
||||
Request.cbBuffer = 1;
|
||||
}
|
||||
|
||||
|
||||
DbgLog((LOG_MEMORY,1,TEXT("Setting Allocator Requirements")));
|
||||
DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d"),
|
||||
Request.cBuffers, Request.cbBuffer));
|
||||
|
||||
// Pass the allocator requirements to our output side
|
||||
// but do a little sanity checking first or we'll just hit
|
||||
// asserts in the allocator.
|
||||
|
||||
pProperties->cBuffers = Request.cBuffers;
|
||||
pProperties->cbBuffer = Request.cbBuffer;
|
||||
pProperties->cbAlign = Request.cbAlign;
|
||||
if (pProperties->cBuffers<=0) {pProperties->cBuffers = 1; }
|
||||
if (pProperties->cbBuffer<=0) {pProperties->cbBuffer = 1; }
|
||||
hr = pAlloc->SetProperties(pProperties, &Actual);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
DbgLog((LOG_MEMORY,1,TEXT("Obtained Allocator Requirements")));
|
||||
DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d, Alignment %d"),
|
||||
Actual.cBuffers, Actual.cbBuffer, Actual.cbAlign));
|
||||
|
||||
// Make sure we got the right alignment and at least the minimum required
|
||||
|
||||
if ( (Request.cBuffers > Actual.cBuffers)
|
||||
|| (Request.cbBuffer > Actual.cbBuffer)
|
||||
|| (Request.cbAlign > Actual.cbAlign)
|
||||
) {
|
||||
return E_FAIL;
|
||||
}
|
||||
return NOERROR;
|
||||
|
||||
} // DecideBufferSize
|
||||
|
||||
//
|
||||
// Copy
|
||||
//
|
||||
// return a pointer to an identical copy of pSample
|
||||
IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource)
|
||||
{
|
||||
IMediaSample * pDest;
|
||||
|
||||
HRESULT hr;
|
||||
REFERENCE_TIME tStart, tStop;
|
||||
const BOOL bTime = S_OK == pSource->GetTime( &tStart, &tStop);
|
||||
|
||||
// this may block for an indeterminate amount of time
|
||||
hr = OutputPin()->PeekAllocator()->GetBuffer(
|
||||
&pDest
|
||||
, bTime ? &tStart : NULL
|
||||
, bTime ? &tStop : NULL
|
||||
, m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0
|
||||
);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ASSERT(pDest);
|
||||
IMediaSample2 *pSample2;
|
||||
if (SUCCEEDED(pDest->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) {
|
||||
HRESULT hr = pSample2->SetProperties(
|
||||
FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer),
|
||||
(PBYTE)m_pInput->SampleProps());
|
||||
pSample2->Release();
|
||||
if (FAILED(hr)) {
|
||||
pDest->Release();
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (bTime) {
|
||||
pDest->SetTime(&tStart, &tStop);
|
||||
}
|
||||
|
||||
if (S_OK == pSource->IsSyncPoint()) {
|
||||
pDest->SetSyncPoint(TRUE);
|
||||
}
|
||||
if (S_OK == pSource->IsDiscontinuity() || m_bSampleSkipped) {
|
||||
pDest->SetDiscontinuity(TRUE);
|
||||
}
|
||||
if (S_OK == pSource->IsPreroll()) {
|
||||
pDest->SetPreroll(TRUE);
|
||||
}
|
||||
|
||||
// Copy the media type
|
||||
AM_MEDIA_TYPE *pMediaType;
|
||||
if (S_OK == pSource->GetMediaType(&pMediaType)) {
|
||||
pDest->SetMediaType(pMediaType);
|
||||
DeleteMediaType( pMediaType );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_bSampleSkipped = FALSE;
|
||||
|
||||
// Copy the sample media times
|
||||
REFERENCE_TIME TimeStart, TimeEnd;
|
||||
if (pSource->GetMediaTime(&TimeStart,&TimeEnd) == NOERROR) {
|
||||
pDest->SetMediaTime(&TimeStart,&TimeEnd);
|
||||
}
|
||||
|
||||
// Copy the actual data length and the actual data.
|
||||
{
|
||||
const long lDataLength = pSource->GetActualDataLength();
|
||||
pDest->SetActualDataLength(lDataLength);
|
||||
|
||||
// Copy the sample data
|
||||
{
|
||||
BYTE *pSourceBuffer, *pDestBuffer;
|
||||
long lSourceSize = pSource->GetSize();
|
||||
long lDestSize = pDest->GetSize();
|
||||
|
||||
ASSERT(lDestSize >= lSourceSize && lDestSize >= lDataLength);
|
||||
|
||||
pSource->GetPointer(&pSourceBuffer);
|
||||
pDest->GetPointer(&pDestBuffer);
|
||||
ASSERT(lDestSize == 0 || pSourceBuffer != NULL && pDestBuffer != NULL);
|
||||
|
||||
CopyMemory( (PVOID) pDestBuffer, (PVOID) pSourceBuffer, lDataLength );
|
||||
}
|
||||
}
|
||||
|
||||
return pDest;
|
||||
|
||||
} // Copy
|
||||
|
||||
|
||||
// override this to customize the transform process
|
||||
|
||||
HRESULT
|
||||
CTransInPlaceFilter::Receive(IMediaSample *pSample)
|
||||
{
|
||||
/* Check for other streams and pass them on */
|
||||
AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
|
||||
if (pProps->dwStreamId != AM_STREAM_MEDIA) {
|
||||
return m_pOutput->Deliver(pSample);
|
||||
}
|
||||
HRESULT hr;
|
||||
|
||||
// Start timing the TransInPlace (if PERF is defined)
|
||||
MSR_START(m_idTransInPlace);
|
||||
|
||||
if (UsingDifferentAllocators()) {
|
||||
|
||||
// We have to copy the data.
|
||||
|
||||
pSample = Copy(pSample);
|
||||
|
||||
if (pSample==NULL) {
|
||||
MSR_STOP(m_idTransInPlace);
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
// have the derived class transform the data
|
||||
hr = Transform(pSample);
|
||||
|
||||
// Stop the clock and log it (if PERF is defined)
|
||||
MSR_STOP(m_idTransInPlace);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_TRACE, 1, TEXT("Error from TransInPlace")));
|
||||
if (UsingDifferentAllocators()) {
|
||||
pSample->Release();
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
// the Transform() function can return S_FALSE to indicate that the
|
||||
// sample should not be delivered; we only deliver the sample if it's
|
||||
// really S_OK (same as NOERROR, of course.)
|
||||
if (hr == NOERROR) {
|
||||
hr = m_pOutput->Deliver(pSample);
|
||||
} else {
|
||||
// But it would be an error to return this private workaround
|
||||
// to the caller ...
|
||||
if (S_FALSE == hr) {
|
||||
// S_FALSE returned from Transform is a PRIVATE agreement
|
||||
// We should return NOERROR from Receive() in this cause because
|
||||
// returning S_FALSE from Receive() means that this is the end
|
||||
// of the stream and no more data should be sent.
|
||||
m_bSampleSkipped = TRUE;
|
||||
if (!m_bQualityChanged) {
|
||||
NotifyEvent(EC_QUALITY_CHANGE,0,0);
|
||||
m_bQualityChanged = TRUE;
|
||||
}
|
||||
hr = NOERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// release the output buffer. If the connected pin still needs it,
|
||||
// it will have addrefed it itself.
|
||||
if (UsingDifferentAllocators()) {
|
||||
pSample->Release();
|
||||
}
|
||||
|
||||
return hr;
|
||||
|
||||
} // Receive
|
||||
|
||||
|
||||
|
||||
// =================================================================
|
||||
// Implements the CTransInPlaceInputPin class
|
||||
// =================================================================
|
||||
|
||||
|
||||
// constructor
|
||||
|
||||
CTransInPlaceInputPin::CTransInPlaceInputPin
|
||||
( TCHAR *pObjectName
|
||||
, CTransInPlaceFilter *pFilter
|
||||
, HRESULT *phr
|
||||
, LPCWSTR pName
|
||||
)
|
||||
: CTransformInputPin(pObjectName,
|
||||
pFilter,
|
||||
phr,
|
||||
pName)
|
||||
, m_bReadOnly(FALSE)
|
||||
, m_pTIPFilter(pFilter)
|
||||
{
|
||||
DbgLog((LOG_TRACE, 2
|
||||
, TEXT("CTransInPlaceInputPin::CTransInPlaceInputPin")));
|
||||
|
||||
} // constructor
|
||||
|
||||
|
||||
// =================================================================
|
||||
// Implements IMemInputPin interface
|
||||
// =================================================================
|
||||
|
||||
|
||||
// If the downstream filter has one then offer that (even if our own output
|
||||
// pin is not using it yet. If the upstream filter chooses it then we will
|
||||
// tell our output pin to ReceiveAllocator).
|
||||
// Else if our output pin is using an allocator then offer that.
|
||||
// ( This could mean offering the upstream filter his own allocator,
|
||||
// it could mean offerring our own
|
||||
// ) or it could mean offering the one from downstream
|
||||
// Else fail to offer any allocator at all.
|
||||
|
||||
STDMETHODIMP CTransInPlaceInputPin::GetAllocator(IMemAllocator ** ppAllocator)
|
||||
{
|
||||
CheckPointer(ppAllocator,E_POINTER);
|
||||
ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));
|
||||
CAutoLock cObjectLock(m_pLock);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
if ( m_pTIPFilter->m_pOutput->IsConnected() ) {
|
||||
// Store the allocator we got
|
||||
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
|
||||
->GetAllocator( ppAllocator );
|
||||
if (SUCCEEDED(hr)) {
|
||||
m_pTIPFilter->OutputPin()->SetAllocator( *ppAllocator );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Help upstream filter (eg TIP filter which is having to do a copy)
|
||||
// by providing a temp allocator here - we'll never use
|
||||
// this allocator because when our output is connected we'll
|
||||
// reconnect this pin
|
||||
hr = CTransformInputPin::GetAllocator( ppAllocator );
|
||||
}
|
||||
return hr;
|
||||
|
||||
} // GetAllocator
|
||||
|
||||
|
||||
|
||||
/* Get told which allocator the upstream output pin is actually going to use */
|
||||
|
||||
|
||||
STDMETHODIMP
|
||||
CTransInPlaceInputPin::NotifyAllocator(
|
||||
IMemAllocator * pAllocator,
|
||||
BOOL bReadOnly)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
CheckPointer(pAllocator,E_POINTER);
|
||||
ValidateReadPtr(pAllocator,sizeof(IMemAllocator));
|
||||
|
||||
CAutoLock cObjectLock(m_pLock);
|
||||
|
||||
m_bReadOnly = bReadOnly;
|
||||
// If we modify data then don't accept the allocator if it's
|
||||
// the same as the output pin's allocator
|
||||
|
||||
// If our output is not connected just accept the allocator
|
||||
// We're never going to use this allocator because when our
|
||||
// output pin is connected we'll reconnect this pin
|
||||
if (!m_pTIPFilter->OutputPin()->IsConnected()) {
|
||||
return CTransformInputPin::NotifyAllocator(pAllocator, bReadOnly);
|
||||
}
|
||||
|
||||
// If the allocator is read-only and we're modifying data
|
||||
// and the allocator is the same as the output pin's
|
||||
// then reject
|
||||
if (bReadOnly && m_pTIPFilter->m_bModifiesData) {
|
||||
IMemAllocator *pOutputAllocator =
|
||||
m_pTIPFilter->OutputPin()->PeekAllocator();
|
||||
|
||||
// Make sure we have an output allocator
|
||||
if (pOutputAllocator == NULL) {
|
||||
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()->
|
||||
GetAllocator(&pOutputAllocator);
|
||||
if(FAILED(hr)) {
|
||||
hr = CreateMemoryAllocator(&pOutputAllocator);
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
m_pTIPFilter->OutputPin()->SetAllocator(pOutputAllocator);
|
||||
pOutputAllocator->Release();
|
||||
}
|
||||
}
|
||||
if (pAllocator == pOutputAllocator) {
|
||||
hr = E_FAIL;
|
||||
} else if(SUCCEEDED(hr)) {
|
||||
// Must copy so set the allocator properties on the output
|
||||
ALLOCATOR_PROPERTIES Props, Actual;
|
||||
hr = pAllocator->GetProperties(&Props);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = pOutputAllocator->SetProperties(&Props, &Actual);
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
if ( (Props.cBuffers > Actual.cBuffers)
|
||||
|| (Props.cbBuffer > Actual.cbBuffer)
|
||||
|| (Props.cbAlign > Actual.cbAlign)
|
||||
) {
|
||||
hr = E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the allocator on the output pin
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
|
||||
->NotifyAllocator( pOutputAllocator, FALSE );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
|
||||
->NotifyAllocator( pAllocator, bReadOnly );
|
||||
if (SUCCEEDED(hr)) {
|
||||
m_pTIPFilter->OutputPin()->SetAllocator( pAllocator );
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
|
||||
// It's possible that the old and the new are the same thing.
|
||||
// AddRef before release ensures that we don't unload it.
|
||||
pAllocator->AddRef();
|
||||
|
||||
if( m_pAllocator != NULL )
|
||||
m_pAllocator->Release();
|
||||
|
||||
m_pAllocator = pAllocator; // We have an allocator for the input pin
|
||||
}
|
||||
|
||||
return hr;
|
||||
|
||||
} // NotifyAllocator
|
||||
|
||||
|
||||
// EnumMediaTypes
|
||||
// - pass through to our downstream filter
|
||||
STDMETHODIMP CTransInPlaceInputPin::EnumMediaTypes( IEnumMediaTypes **ppEnum )
|
||||
{
|
||||
// Can only pass through if connected
|
||||
if( !m_pTIPFilter->m_pOutput->IsConnected() )
|
||||
return VFW_E_NOT_CONNECTED;
|
||||
|
||||
return m_pTIPFilter->m_pOutput->GetConnected()->EnumMediaTypes( ppEnum );
|
||||
|
||||
} // EnumMediaTypes
|
||||
|
||||
|
||||
// CheckMediaType
|
||||
// - agree to anything if not connected,
|
||||
// otherwise pass through to the downstream filter.
|
||||
// This assumes that the filter does not change the media type.
|
||||
|
||||
HRESULT CTransInPlaceInputPin::CheckMediaType(const CMediaType *pmt )
|
||||
{
|
||||
HRESULT hr = m_pTIPFilter->CheckInputType(pmt);
|
||||
if (hr!=S_OK) return hr;
|
||||
|
||||
if( m_pTIPFilter->m_pOutput->IsConnected() )
|
||||
return m_pTIPFilter->m_pOutput->GetConnected()->QueryAccept( pmt );
|
||||
else
|
||||
return S_OK;
|
||||
|
||||
} // CheckMediaType
|
||||
|
||||
|
||||
// If upstream asks us what our requirements are, we will try to ask downstream
|
||||
// if that doesn't work, we'll just take the defaults.
|
||||
STDMETHODIMP
|
||||
CTransInPlaceInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps)
|
||||
{
|
||||
|
||||
if( m_pTIPFilter->m_pOutput->IsConnected() )
|
||||
return m_pTIPFilter->OutputPin()
|
||||
->ConnectedIMemInputPin()->GetAllocatorRequirements( pProps );
|
||||
else
|
||||
return E_NOTIMPL;
|
||||
|
||||
} // GetAllocatorRequirements
|
||||
|
||||
|
||||
// CTransInPlaceInputPin::CompleteConnect() calls CBaseInputPin::CompleteConnect()
|
||||
// and then calls CTransInPlaceFilter::CompleteConnect(). It does this because
|
||||
// CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not
|
||||
// want to reconnect a pin if CBaseInputPin::CompleteConnect() fails.
|
||||
HRESULT
|
||||
CTransInPlaceInputPin::CompleteConnect(IPin *pReceivePin)
|
||||
{
|
||||
HRESULT hr = CBaseInputPin::CompleteConnect(pReceivePin);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin);
|
||||
} // CompleteConnect
|
||||
|
||||
|
||||
// =================================================================
|
||||
// Implements the CTransInPlaceOutputPin class
|
||||
// =================================================================
|
||||
|
||||
|
||||
// constructor
|
||||
|
||||
CTransInPlaceOutputPin::CTransInPlaceOutputPin(
|
||||
TCHAR *pObjectName,
|
||||
CTransInPlaceFilter *pFilter,
|
||||
HRESULT * phr,
|
||||
LPCWSTR pPinName)
|
||||
: CTransformOutputPin( pObjectName
|
||||
, pFilter
|
||||
, phr
|
||||
, pPinName),
|
||||
m_pTIPFilter(pFilter)
|
||||
{
|
||||
DbgLog(( LOG_TRACE, 2
|
||||
, TEXT("CTransInPlaceOutputPin::CTransInPlaceOutputPin")));
|
||||
|
||||
} // constructor
|
||||
|
||||
|
||||
// EnumMediaTypes
|
||||
// - pass through to our upstream filter
|
||||
STDMETHODIMP CTransInPlaceOutputPin::EnumMediaTypes( IEnumMediaTypes **ppEnum )
|
||||
{
|
||||
// Can only pass through if connected.
|
||||
if( ! m_pTIPFilter->m_pInput->IsConnected() )
|
||||
return VFW_E_NOT_CONNECTED;
|
||||
|
||||
return m_pTIPFilter->m_pInput->GetConnected()->EnumMediaTypes( ppEnum );
|
||||
|
||||
} // EnumMediaTypes
|
||||
|
||||
|
||||
|
||||
// CheckMediaType
|
||||
// - agree to anything if not connected,
|
||||
// otherwise pass through to the upstream filter.
|
||||
|
||||
HRESULT CTransInPlaceOutputPin::CheckMediaType(const CMediaType *pmt )
|
||||
{
|
||||
// Don't accept any output pin type changes if we're copying
|
||||
// between allocators - it's too late to change the input
|
||||
// allocator size.
|
||||
if (m_pTIPFilter->UsingDifferentAllocators() && !m_pFilter->IsStopped()) {
|
||||
if (*pmt == m_mt) {
|
||||
return S_OK;
|
||||
} else {
|
||||
return VFW_E_TYPE_NOT_ACCEPTED;
|
||||
}
|
||||
}
|
||||
|
||||
// Assumes the type does not change. That's why we're calling
|
||||
// CheckINPUTType here on the OUTPUT pin.
|
||||
HRESULT hr = m_pTIPFilter->CheckInputType(pmt);
|
||||
if (hr!=S_OK) return hr;
|
||||
|
||||
if( m_pTIPFilter->m_pInput->IsConnected() )
|
||||
return m_pTIPFilter->m_pInput->GetConnected()->QueryAccept( pmt );
|
||||
else
|
||||
return S_OK;
|
||||
|
||||
} // CheckMediaType
|
||||
|
||||
|
||||
/* Save the allocator pointer in the output pin
|
||||
*/
|
||||
void
|
||||
CTransInPlaceOutputPin::SetAllocator(IMemAllocator * pAllocator)
|
||||
{
|
||||
pAllocator->AddRef();
|
||||
if (m_pAllocator) {
|
||||
m_pAllocator->Release();
|
||||
}
|
||||
m_pAllocator = pAllocator;
|
||||
} // SetAllocator
|
||||
|
||||
|
||||
// CTransInPlaceOutputPin::CompleteConnect() calls CBaseOutputPin::CompleteConnect()
|
||||
// and then calls CTransInPlaceFilter::CompleteConnect(). It does this because
|
||||
// CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not want to
|
||||
// reconnect a pin if CBaseOutputPin::CompleteConnect() fails.
|
||||
// CBaseOutputPin::CompleteConnect() often fails when our output pin is being connected
|
||||
// to the Video Mixing Renderer.
|
||||
HRESULT
|
||||
CTransInPlaceOutputPin::CompleteConnect(IPin *pReceivePin)
|
||||
{
|
||||
HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin);
|
||||
} // CompleteConnect
|
@ -1,250 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: TransIP.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines classes from which simple
|
||||
// Transform-In-Place filters may be derived.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// The difference between this and Transfrm.h is that Transfrm copies the data.
|
||||
//
|
||||
// It assumes the filter has one input and one output stream, and has no
|
||||
// interest in memory management, interface negotiation or anything else.
|
||||
//
|
||||
// Derive your class from this, and supply Transform and the media type/format
|
||||
// negotiation functions. Implement that class, compile and link and
|
||||
// you're done.
|
||||
|
||||
|
||||
#ifndef __TRANSIP__
|
||||
#define __TRANSIP__
|
||||
|
||||
// ======================================================================
|
||||
// This is the com object that represents a simple transform filter. It
|
||||
// supports IBaseFilter, IMediaFilter and two pins through nested interfaces
|
||||
// ======================================================================
|
||||
|
||||
class CTransInPlaceFilter;
|
||||
|
||||
// Several of the pin functions call filter functions to do the work,
|
||||
// so you can often use the pin classes unaltered, just overriding the
|
||||
// functions in CTransInPlaceFilter. If that's not enough and you want
|
||||
// to derive your own pin class, override GetPin in the filter to supply
|
||||
// your own pin classes to the filter.
|
||||
|
||||
// ==================================================
|
||||
// Implements the input pin
|
||||
// ==================================================
|
||||
|
||||
class CTransInPlaceInputPin : public CTransformInputPin
|
||||
{
|
||||
|
||||
protected:
|
||||
CTransInPlaceFilter * const m_pTIPFilter; // our filter
|
||||
BOOL m_bReadOnly; // incoming stream is read only
|
||||
|
||||
public:
|
||||
|
||||
CTransInPlaceInputPin(
|
||||
TCHAR *pObjectName,
|
||||
CTransInPlaceFilter *pFilter,
|
||||
HRESULT *phr,
|
||||
LPCWSTR pName);
|
||||
|
||||
// --- IMemInputPin -----
|
||||
|
||||
// Provide an enumerator for media types by getting one from downstream
|
||||
STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum );
|
||||
|
||||
// Say whether media type is acceptable.
|
||||
HRESULT CheckMediaType(const CMediaType* pmt);
|
||||
|
||||
// Return our upstream allocator
|
||||
STDMETHODIMP GetAllocator(IMemAllocator ** ppAllocator);
|
||||
|
||||
// get told which allocator the upstream output pin is actually
|
||||
// going to use.
|
||||
STDMETHODIMP NotifyAllocator(IMemAllocator * pAllocator,
|
||||
BOOL bReadOnly);
|
||||
|
||||
// Allow the filter to see what allocator we have
|
||||
// N.B. This does NOT AddRef
|
||||
IMemAllocator * PeekAllocator() const
|
||||
{ return m_pAllocator; }
|
||||
|
||||
// Pass this on downstream if it ever gets called.
|
||||
STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps);
|
||||
|
||||
HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
|
||||
inline const BOOL ReadOnly() { return m_bReadOnly ; }
|
||||
|
||||
}; // CTransInPlaceInputPin
|
||||
|
||||
// ==================================================
|
||||
// Implements the output pin
|
||||
// ==================================================
|
||||
|
||||
class CTransInPlaceOutputPin : public CTransformOutputPin
|
||||
{
|
||||
|
||||
protected:
|
||||
// m_pFilter points to our CBaseFilter
|
||||
CTransInPlaceFilter * const m_pTIPFilter;
|
||||
|
||||
public:
|
||||
|
||||
CTransInPlaceOutputPin(
|
||||
TCHAR *pObjectName,
|
||||
CTransInPlaceFilter *pFilter,
|
||||
HRESULT *phr,
|
||||
LPCWSTR pName);
|
||||
|
||||
|
||||
// --- CBaseOutputPin ------------
|
||||
|
||||
// negotiate the allocator and its buffer size/count
|
||||
// Insists on using our own allocator. (Actually the one upstream of us).
|
||||
// We don't override this - instead we just agree the default
|
||||
// then let the upstream filter decide for itself on reconnect
|
||||
// virtual HRESULT DecideAllocator(IMemInputPin * pPin, IMemAllocator ** pAlloc);
|
||||
|
||||
// Provide a media type enumerator. Get it from upstream.
|
||||
STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum );
|
||||
|
||||
// Say whether media type is acceptable.
|
||||
HRESULT CheckMediaType(const CMediaType* pmt);
|
||||
|
||||
// This just saves the allocator being used on the output pin
|
||||
// Also called by input pin's GetAllocator()
|
||||
void SetAllocator(IMemAllocator * pAllocator);
|
||||
|
||||
IMemInputPin * ConnectedIMemInputPin()
|
||||
{ return m_pInputPin; }
|
||||
|
||||
// Allow the filter to see what allocator we have
|
||||
// N.B. This does NOT AddRef
|
||||
IMemAllocator * PeekAllocator() const
|
||||
{ return m_pAllocator; }
|
||||
|
||||
HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
|
||||
}; // CTransInPlaceOutputPin
|
||||
|
||||
|
||||
class AM_NOVTABLE CTransInPlaceFilter : public CTransformFilter
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// map getpin/getpincount for base enum of pins to owner
|
||||
// override this to return more specialised pin objects
|
||||
|
||||
virtual CBasePin *GetPin(int n);
|
||||
|
||||
public:
|
||||
|
||||
// Set bModifiesData == false if your derived filter does
|
||||
// not modify the data samples (for instance it's just copying
|
||||
// them somewhere else or looking at the timestamps).
|
||||
|
||||
CTransInPlaceFilter(TCHAR *, LPUNKNOWN, REFCLSID clsid, HRESULT *,
|
||||
bool bModifiesData = true);
|
||||
#ifdef UNICODE
|
||||
CTransInPlaceFilter(CHAR *, LPUNKNOWN, REFCLSID clsid, HRESULT *,
|
||||
bool bModifiesData = true);
|
||||
#endif
|
||||
// The following are defined to avoid undefined pure virtuals.
|
||||
// Even if they are never called, they will give linkage warnings/errors
|
||||
|
||||
// We override EnumMediaTypes to bypass the transform class enumerator
|
||||
// which would otherwise call this.
|
||||
HRESULT GetMediaType(int iPosition, CMediaType *pMediaType)
|
||||
{ DbgBreak("CTransInPlaceFilter::GetMediaType should never be called");
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
// This is called when we actually have to provide out own allocator.
|
||||
HRESULT DecideBufferSize(IMemAllocator*, ALLOCATOR_PROPERTIES *);
|
||||
|
||||
// The functions which call this in CTransform are overridden in this
|
||||
// class to call CheckInputType with the assumption that the type
|
||||
// does not change. In Debug builds some calls will be made and
|
||||
// we just ensure that they do not assert.
|
||||
HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut)
|
||||
{
|
||||
return S_OK;
|
||||
};
|
||||
|
||||
|
||||
// =================================================================
|
||||
// ----- You may want to override this -----------------------------
|
||||
// =================================================================
|
||||
|
||||
HRESULT CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin);
|
||||
|
||||
// chance to customize the transform process
|
||||
virtual HRESULT Receive(IMediaSample *pSample);
|
||||
|
||||
// =================================================================
|
||||
// ----- You MUST override these -----------------------------------
|
||||
// =================================================================
|
||||
|
||||
virtual HRESULT Transform(IMediaSample *pSample) PURE;
|
||||
|
||||
// this goes in the factory template table to create new instances
|
||||
// static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
|
||||
|
||||
|
||||
#ifdef PERF
|
||||
// Override to register performance measurement with a less generic string
|
||||
// You should do this to avoid confusion with other filters
|
||||
virtual void RegisterPerfId()
|
||||
{m_idTransInPlace = MSR_REGISTER(TEXT("TransInPlace"));}
|
||||
#endif // PERF
|
||||
|
||||
|
||||
// implementation details
|
||||
|
||||
protected:
|
||||
|
||||
IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource);
|
||||
|
||||
#ifdef PERF
|
||||
int m_idTransInPlace; // performance measuring id
|
||||
#endif // PERF
|
||||
bool m_bModifiesData; // Does this filter change the data?
|
||||
|
||||
// these hold our input and output pins
|
||||
|
||||
friend class CTransInPlaceInputPin;
|
||||
friend class CTransInPlaceOutputPin;
|
||||
|
||||
CTransInPlaceInputPin *InputPin() const
|
||||
{
|
||||
return (CTransInPlaceInputPin *)m_pInput;
|
||||
};
|
||||
CTransInPlaceOutputPin *OutputPin() const
|
||||
{
|
||||
return (CTransInPlaceOutputPin *)m_pOutput;
|
||||
};
|
||||
|
||||
// Helper to see if the input and output types match
|
||||
BOOL TypesMatch()
|
||||
{
|
||||
return InputPin()->CurrentMediaType() ==
|
||||
OutputPin()->CurrentMediaType();
|
||||
}
|
||||
|
||||
// Are the input and output allocators different?
|
||||
BOOL UsingDifferentAllocators() const
|
||||
{
|
||||
return InputPin()->PeekAllocator() != OutputPin()->PeekAllocator();
|
||||
}
|
||||
}; // CTransInPlaceFilter
|
||||
|
||||
#endif /* __TRANSIP__ */
|
||||
|
@ -1,468 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Vtrans.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include "streams.h"
|
||||
#include "measure.h"
|
||||
// #include <vtransfr.h> // now in precomp file streams.h
|
||||
|
||||
CVideoTransformFilter::CVideoTransformFilter
|
||||
( TCHAR *pName, LPUNKNOWN pUnk, REFCLSID clsid)
|
||||
: CTransformFilter(pName, pUnk, clsid)
|
||||
, m_itrLate(0)
|
||||
, m_nKeyFramePeriod(0) // No QM until we see at least 2 key frames
|
||||
, m_nFramesSinceKeyFrame(0)
|
||||
, m_bSkipping(FALSE)
|
||||
, m_tDecodeStart(0)
|
||||
, m_itrAvgDecode(300000) // 30mSec - probably allows skipping
|
||||
, m_bQualityChanged(FALSE)
|
||||
{
|
||||
#ifdef PERF
|
||||
RegisterPerfId();
|
||||
#endif // PERF
|
||||
}
|
||||
|
||||
|
||||
CVideoTransformFilter::~CVideoTransformFilter()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
|
||||
// Reset our quality management state
|
||||
|
||||
HRESULT CVideoTransformFilter::StartStreaming()
|
||||
{
|
||||
m_itrLate = 0;
|
||||
m_nKeyFramePeriod = 0; // No QM until we see at least 2 key frames
|
||||
m_nFramesSinceKeyFrame = 0;
|
||||
m_bSkipping = FALSE;
|
||||
m_tDecodeStart = 0;
|
||||
m_itrAvgDecode = 300000; // 30mSec - probably allows skipping
|
||||
m_bQualityChanged = FALSE;
|
||||
m_bSampleSkipped = FALSE;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// Overriden to reset quality management information
|
||||
|
||||
HRESULT CVideoTransformFilter::EndFlush()
|
||||
{
|
||||
{
|
||||
// Synchronize
|
||||
CAutoLock lck(&m_csReceive);
|
||||
|
||||
// Reset our stats
|
||||
//
|
||||
// Note - we don't want to call derived classes here,
|
||||
// we only want to reset our internal variables and this
|
||||
// is a convenient way to do it
|
||||
CVideoTransformFilter::StartStreaming();
|
||||
}
|
||||
return CTransformFilter::EndFlush();
|
||||
}
|
||||
|
||||
|
||||
HRESULT CVideoTransformFilter::AbortPlayback(HRESULT hr)
|
||||
{
|
||||
NotifyEvent(EC_ERRORABORT, hr, 0);
|
||||
m_pOutput->DeliverEndOfStream();
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
// Receive()
|
||||
//
|
||||
// Accept a sample from upstream, decide whether to process it
|
||||
// or drop it. If we process it then get a buffer from the
|
||||
// allocator of the downstream connection, transform it into the
|
||||
// new buffer and deliver it to the downstream filter.
|
||||
// If we decide not to process it then we do not get a buffer.
|
||||
|
||||
// Remember that although this code will notice format changes coming into
|
||||
// the input pin, it will NOT change its output format if that results
|
||||
// in the filter needing to make a corresponding output format change. Your
|
||||
// derived filter will have to take care of that. (eg. a palette change if
|
||||
// the input and output is an 8 bit format). If the input sample is discarded
|
||||
// and nothing is sent out for this Receive, please remember to put the format
|
||||
// change on the first output sample that you actually do send.
|
||||
// If your filter will produce the same output type even when the input type
|
||||
// changes, then this base class code will do everything you need.
|
||||
|
||||
HRESULT CVideoTransformFilter::Receive(IMediaSample *pSample)
|
||||
{
|
||||
// If the next filter downstream is the video renderer, then it may
|
||||
// be able to operate in DirectDraw mode which saves copying the data
|
||||
// and gives higher performance. In that case the buffer which we
|
||||
// get from GetDeliveryBuffer will be a DirectDraw buffer, and
|
||||
// drawing into this buffer draws directly onto the display surface.
|
||||
// This means that any waiting for the correct time to draw occurs
|
||||
// during GetDeliveryBuffer, and that once the buffer is given to us
|
||||
// the video renderer will count it in its statistics as a frame drawn.
|
||||
// This means that any decision to drop the frame must be taken before
|
||||
// calling GetDeliveryBuffer.
|
||||
|
||||
ASSERT(CritCheckIn(&m_csReceive));
|
||||
AM_MEDIA_TYPE *pmtOut, *pmt;
|
||||
#ifdef DEBUG
|
||||
FOURCCMap fccOut;
|
||||
#endif
|
||||
HRESULT hr;
|
||||
ASSERT(pSample);
|
||||
IMediaSample * pOutSample;
|
||||
|
||||
// If no output pin to deliver to then no point sending us data
|
||||
ASSERT (m_pOutput != NULL) ;
|
||||
|
||||
// The source filter may dynamically ask us to start transforming from a
|
||||
// different media type than the one we're using now. If we don't, we'll
|
||||
// draw garbage. (typically, this is a palette change in the movie,
|
||||
// but could be something more sinister like the compression type changing,
|
||||
// or even the video size changing)
|
||||
|
||||
#define rcS1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcSource
|
||||
#define rcT1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcTarget
|
||||
|
||||
pSample->GetMediaType(&pmt);
|
||||
if (pmt != NULL && pmt->pbFormat != NULL) {
|
||||
|
||||
// spew some debug output
|
||||
ASSERT(!IsEqualGUID(pmt->majortype, GUID_NULL));
|
||||
#ifdef DEBUG
|
||||
fccOut.SetFOURCC(&pmt->subtype);
|
||||
LONG lCompression = HEADER(pmt->pbFormat)->biCompression;
|
||||
LONG lBitCount = HEADER(pmt->pbFormat)->biBitCount;
|
||||
LONG lStride = (HEADER(pmt->pbFormat)->biWidth * lBitCount + 7) / 8;
|
||||
lStride = (lStride + 3) & ~3;
|
||||
DbgLog((LOG_TRACE,3,TEXT("*Changing input type on the fly to")));
|
||||
DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"),
|
||||
fccOut.GetFOURCC(), lCompression, lBitCount));
|
||||
DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"),
|
||||
HEADER(pmt->pbFormat)->biHeight,
|
||||
rcT1.left, rcT1.top, rcT1.right, rcT1.bottom));
|
||||
DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"),
|
||||
rcS1.left, rcS1.top, rcS1.right, rcS1.bottom,
|
||||
lStride));
|
||||
#endif
|
||||
|
||||
// now switch to using the new format. I am assuming that the
|
||||
// derived filter will do the right thing when its media type is
|
||||
// switched and streaming is restarted.
|
||||
|
||||
StopStreaming();
|
||||
m_pInput->CurrentMediaType() = *pmt;
|
||||
DeleteMediaType(pmt);
|
||||
// if this fails, playback will stop, so signal an error
|
||||
hr = StartStreaming();
|
||||
if (FAILED(hr)) {
|
||||
return AbortPlayback(hr);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have noticed any format changes on the input sample, it's
|
||||
// OK to discard it.
|
||||
|
||||
if (ShouldSkipFrame(pSample)) {
|
||||
MSR_NOTE(m_idSkip);
|
||||
m_bSampleSkipped = TRUE;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
// Set up the output sample
|
||||
hr = InitializeOutputSample(pSample, &pOutSample);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
m_bSampleSkipped = FALSE;
|
||||
|
||||
// The renderer may ask us to on-the-fly to start transforming to a
|
||||
// different format. If we don't obey it, we'll draw garbage
|
||||
|
||||
#define rcS ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcSource
|
||||
#define rcT ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcTarget
|
||||
|
||||
pOutSample->GetMediaType(&pmtOut);
|
||||
if (pmtOut != NULL && pmtOut->pbFormat != NULL) {
|
||||
|
||||
// spew some debug output
|
||||
ASSERT(!IsEqualGUID(pmtOut->majortype, GUID_NULL));
|
||||
#ifdef DEBUG
|
||||
fccOut.SetFOURCC(&pmtOut->subtype);
|
||||
LONG lCompression = HEADER(pmtOut->pbFormat)->biCompression;
|
||||
LONG lBitCount = HEADER(pmtOut->pbFormat)->biBitCount;
|
||||
LONG lStride = (HEADER(pmtOut->pbFormat)->biWidth * lBitCount + 7) / 8;
|
||||
lStride = (lStride + 3) & ~3;
|
||||
DbgLog((LOG_TRACE,3,TEXT("*Changing output type on the fly to")));
|
||||
DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"),
|
||||
fccOut.GetFOURCC(), lCompression, lBitCount));
|
||||
DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"),
|
||||
HEADER(pmtOut->pbFormat)->biHeight,
|
||||
rcT.left, rcT.top, rcT.right, rcT.bottom));
|
||||
DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"),
|
||||
rcS.left, rcS.top, rcS.right, rcS.bottom,
|
||||
lStride));
|
||||
#endif
|
||||
|
||||
// now switch to using the new format. I am assuming that the
|
||||
// derived filter will do the right thing when its media type is
|
||||
// switched and streaming is restarted.
|
||||
|
||||
StopStreaming();
|
||||
m_pOutput->CurrentMediaType() = *pmtOut;
|
||||
DeleteMediaType(pmtOut);
|
||||
hr = StartStreaming();
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
// a new format, means a new empty buffer, so wait for a keyframe
|
||||
// before passing anything on to the renderer.
|
||||
// !!! a keyframe may never come, so give up after 30 frames
|
||||
DbgLog((LOG_TRACE,3,TEXT("Output format change means we must wait for a keyframe")));
|
||||
m_nWaitForKey = 30;
|
||||
|
||||
// if this fails, playback will stop, so signal an error
|
||||
} else {
|
||||
|
||||
// Must release the sample before calling AbortPlayback
|
||||
// because we might be holding the win16 lock or
|
||||
// ddraw lock
|
||||
pOutSample->Release();
|
||||
AbortPlayback(hr);
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
// After a discontinuity, we need to wait for the next key frame
|
||||
if (pSample->IsDiscontinuity() == S_OK) {
|
||||
DbgLog((LOG_TRACE,3,TEXT("Non-key discontinuity - wait for keyframe")));
|
||||
m_nWaitForKey = 30;
|
||||
}
|
||||
|
||||
// Start timing the transform (and log it if PERF is defined)
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
m_tDecodeStart = timeGetTime();
|
||||
MSR_START(m_idTransform);
|
||||
|
||||
// have the derived class transform the data
|
||||
hr = Transform(pSample, pOutSample);
|
||||
|
||||
// Stop the clock (and log it if PERF is defined)
|
||||
MSR_STOP(m_idTransform);
|
||||
m_tDecodeStart = timeGetTime()-m_tDecodeStart;
|
||||
m_itrAvgDecode = m_tDecodeStart*(10000/16) + 15*(m_itrAvgDecode/16);
|
||||
|
||||
// Maybe we're waiting for a keyframe still?
|
||||
if (m_nWaitForKey)
|
||||
m_nWaitForKey--;
|
||||
if (m_nWaitForKey && pSample->IsSyncPoint() == S_OK)
|
||||
m_nWaitForKey = FALSE;
|
||||
|
||||
// if so, then we don't want to pass this on to the renderer
|
||||
if (m_nWaitForKey && hr == NOERROR) {
|
||||
DbgLog((LOG_TRACE,3,TEXT("still waiting for a keyframe")));
|
||||
hr = S_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
DbgLog((LOG_TRACE,1,TEXT("Error from video transform")));
|
||||
} else {
|
||||
// the Transform() function can return S_FALSE to indicate that the
|
||||
// sample should not be delivered; we only deliver the sample if it's
|
||||
// really S_OK (same as NOERROR, of course.)
|
||||
// Try not to return S_FALSE to a direct draw buffer (it's wasteful)
|
||||
// Try to take the decision earlier - before you get it.
|
||||
|
||||
if (hr == NOERROR) {
|
||||
hr = m_pOutput->Deliver(pOutSample);
|
||||
} else {
|
||||
// S_FALSE returned from Transform is a PRIVATE agreement
|
||||
// We should return NOERROR from Receive() in this case because returning S_FALSE
|
||||
// from Receive() means that this is the end of the stream and no more data should
|
||||
// be sent.
|
||||
if (S_FALSE == hr) {
|
||||
|
||||
// We must Release() the sample before doing anything
|
||||
// like calling the filter graph because having the
|
||||
// sample means we may have the DirectDraw lock
|
||||
// (== win16 lock on some versions)
|
||||
pOutSample->Release();
|
||||
m_bSampleSkipped = TRUE;
|
||||
if (!m_bQualityChanged) {
|
||||
m_bQualityChanged = TRUE;
|
||||
NotifyEvent(EC_QUALITY_CHANGE,0,0);
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// release the output buffer. If the connected pin still needs it,
|
||||
// it will have addrefed it itself.
|
||||
pOutSample->Release();
|
||||
ASSERT(CritCheckIn(&m_csReceive));
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL CVideoTransformFilter::ShouldSkipFrame( IMediaSample * pIn)
|
||||
{
|
||||
REFERENCE_TIME trStart, trStopAt;
|
||||
HRESULT hr = pIn->GetTime(&trStart, &trStopAt);
|
||||
|
||||
// Don't skip frames with no timestamps
|
||||
if (hr != S_OK)
|
||||
return FALSE;
|
||||
|
||||
int itrFrame = (int)(trStopAt - trStart); // frame duration
|
||||
|
||||
if(S_OK==pIn->IsSyncPoint()) {
|
||||
MSR_INTEGER(m_idFrameType, 1);
|
||||
if ( m_nKeyFramePeriod < m_nFramesSinceKeyFrame ) {
|
||||
// record the max
|
||||
m_nKeyFramePeriod = m_nFramesSinceKeyFrame;
|
||||
}
|
||||
m_nFramesSinceKeyFrame = 0;
|
||||
m_bSkipping = FALSE;
|
||||
} else {
|
||||
MSR_INTEGER(m_idFrameType, 2);
|
||||
if ( m_nFramesSinceKeyFrame>m_nKeyFramePeriod
|
||||
&& m_nKeyFramePeriod>0
|
||||
) {
|
||||
// We haven't seen the key frame yet, but we were clearly being
|
||||
// overoptimistic about how frequent they are.
|
||||
m_nKeyFramePeriod = m_nFramesSinceKeyFrame;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Whatever we might otherwise decide,
|
||||
// if we are taking only a small fraction of the required frame time to decode
|
||||
// then any quality problems are actually coming from somewhere else.
|
||||
// Could be a net problem at the source for instance. In this case there's
|
||||
// no point in us skipping frames here.
|
||||
if (m_itrAvgDecode*4>itrFrame) {
|
||||
|
||||
// Don't skip unless we are at least a whole frame late.
|
||||
// (We would skip B frames if more than 1/2 frame late, but they're safe).
|
||||
if ( m_itrLate > itrFrame ) {
|
||||
|
||||
// Don't skip unless the anticipated key frame would be no more than
|
||||
// 1 frame early. If the renderer has not been waiting (we *guess*
|
||||
// it hasn't because we're late) then it will allow frames to be
|
||||
// played early by up to a frame.
|
||||
|
||||
// Let T = Stream time from now to anticipated next key frame
|
||||
// = (frame duration) * (KeyFramePeriod - FramesSinceKeyFrame)
|
||||
// So we skip if T - Late < one frame i.e.
|
||||
// (duration) * (freq - FramesSince) - Late < duration
|
||||
// or (duration) * (freq - FramesSince - 1) < Late
|
||||
|
||||
// We don't dare skip until we have seen some key frames and have
|
||||
// some idea how often they occur and they are reasonably frequent.
|
||||
if (m_nKeyFramePeriod>0) {
|
||||
// It would be crazy - but we could have a stream with key frames
|
||||
// a very long way apart - and if they are further than about
|
||||
// 3.5 minutes apart then we could get arithmetic overflow in
|
||||
// reference time units. Therefore we switch to mSec at this point
|
||||
int it = (itrFrame/10000)
|
||||
* (m_nKeyFramePeriod-m_nFramesSinceKeyFrame - 1);
|
||||
MSR_INTEGER(m_idTimeTillKey, it);
|
||||
|
||||
// For debug - might want to see the details - dump them as scratch pad
|
||||
#ifdef VTRANSPERF
|
||||
MSR_INTEGER(0, itrFrame);
|
||||
MSR_INTEGER(0, m_nFramesSinceKeyFrame);
|
||||
MSR_INTEGER(0, m_nKeyFramePeriod);
|
||||
#endif
|
||||
if (m_itrLate/10000 > it) {
|
||||
m_bSkipping = TRUE;
|
||||
// Now we are committed. Once we start skipping, we
|
||||
// cannot stop until we hit a key frame.
|
||||
} else {
|
||||
#ifdef VTRANSPERF
|
||||
MSR_INTEGER(0, 777770); // not near enough to next key
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
#ifdef VTRANSPERF
|
||||
MSR_INTEGER(0, 777771); // Next key not predictable
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
#ifdef VTRANSPERF
|
||||
MSR_INTEGER(0, 777772); // Less than one frame late
|
||||
MSR_INTEGER(0, m_itrLate);
|
||||
MSR_INTEGER(0, itrFrame);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
#ifdef VTRANSPERF
|
||||
MSR_INTEGER(0, 777773); // Decode time short - not not worth skipping
|
||||
MSR_INTEGER(0, m_itrAvgDecode);
|
||||
MSR_INTEGER(0, itrFrame);
|
||||
#endif
|
||||
}
|
||||
|
||||
++m_nFramesSinceKeyFrame;
|
||||
|
||||
if (m_bSkipping) {
|
||||
// We will count down the lateness as we skip each frame.
|
||||
// We re-assess each frame. The key frame might not arrive when expected.
|
||||
// We reset m_itrLate if we get a new Quality message, but actually that's
|
||||
// not likely because we're not sending frames on to the Renderer. In
|
||||
// fact if we DID get another one it would mean that there's a long
|
||||
// pipe between us and the renderer and we might need an altogether
|
||||
// better strategy to avoid hunting!
|
||||
m_itrLate = m_itrLate - itrFrame;
|
||||
}
|
||||
|
||||
MSR_INTEGER(m_idLate, (int)m_itrLate/10000 ); // Note how late we think we are
|
||||
if (m_bSkipping) {
|
||||
if (!m_bQualityChanged) {
|
||||
m_bQualityChanged = TRUE;
|
||||
NotifyEvent(EC_QUALITY_CHANGE,0,0);
|
||||
}
|
||||
}
|
||||
return m_bSkipping;
|
||||
}
|
||||
|
||||
|
||||
HRESULT CVideoTransformFilter::AlterQuality(Quality q)
|
||||
{
|
||||
// to reduce the amount of 64 bit arithmetic, m_itrLate is an int.
|
||||
// +, -, >, == etc are not too bad, but * and / are painful.
|
||||
if (m_itrLate>300000000) {
|
||||
// Avoid overflow and silliness - more than 30 secs late is already silly
|
||||
m_itrLate = 300000000;
|
||||
} else {
|
||||
m_itrLate = (int)q.Late;
|
||||
}
|
||||
// We ignore the other fields
|
||||
|
||||
// We're actually not very good at handling this. In non-direct draw mode
|
||||
// most of the time can be spent in the renderer which can skip any frame.
|
||||
// In that case we'd rather the renderer handled things.
|
||||
// Nevertheless we will keep an eye on it and if we really start getting
|
||||
// a very long way behind then we will actually skip - but we'll still tell
|
||||
// the renderer (or whoever is downstream) that they should handle quality.
|
||||
|
||||
return E_FAIL; // Tell the renderer to do his thing.
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This will avoid several hundred useless warnings if compiled -W4 by MS VC++ v4
|
||||
#pragma warning(disable:4514)
|
||||
|
@ -1,143 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: VTrans.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a video transform class.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// This class is derived from CTransformFilter, but is specialised to handle
|
||||
// the requirements of video quality control by frame dropping.
|
||||
// This is a non-in-place transform, (i.e. it copies the data) such as a decoder.
|
||||
|
||||
class CVideoTransformFilter : public CTransformFilter
|
||||
{
|
||||
public:
|
||||
|
||||
CVideoTransformFilter(TCHAR *, LPUNKNOWN, REFCLSID clsid);
|
||||
~CVideoTransformFilter();
|
||||
HRESULT EndFlush();
|
||||
|
||||
// =================================================================
|
||||
// ----- override these bits ---------------------------------------
|
||||
// =================================================================
|
||||
// The following methods are in CTransformFilter which is inherited.
|
||||
// They are mentioned here for completeness
|
||||
//
|
||||
// These MUST be supplied in a derived class
|
||||
//
|
||||
// NOTE:
|
||||
// virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);
|
||||
// virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;
|
||||
// virtual HRESULT CheckTransform
|
||||
// (const CMediaType* mtIn, const CMediaType* mtOut) PURE;
|
||||
// static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
|
||||
// virtual HRESULT DecideBufferSize
|
||||
// (IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop) PURE;
|
||||
// virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE;
|
||||
//
|
||||
// These MAY also be overridden
|
||||
//
|
||||
// virtual HRESULT StopStreaming();
|
||||
// virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);
|
||||
// virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);
|
||||
// virtual HRESULT BreakConnect(PIN_DIRECTION dir);
|
||||
// virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);
|
||||
// virtual HRESULT EndOfStream(void);
|
||||
// virtual HRESULT BeginFlush(void);
|
||||
// virtual HRESULT EndFlush(void);
|
||||
// virtual HRESULT NewSegment
|
||||
// (REFERENCE_TIME tStart,REFERENCE_TIME tStop,double dRate);
|
||||
#ifdef PERF
|
||||
|
||||
// If you override this - ensure that you register all these ids
|
||||
// as well as any of your own,
|
||||
virtual void RegisterPerfId() {
|
||||
m_idSkip = MSR_REGISTER(TEXT("Video Transform Skip frame"));
|
||||
m_idFrameType = MSR_REGISTER(TEXT("Video transform frame type"));
|
||||
m_idLate = MSR_REGISTER(TEXT("Video Transform Lateness"));
|
||||
m_idTimeTillKey = MSR_REGISTER(TEXT("Video Transform Estd. time to next key"));
|
||||
CTransformFilter::RegisterPerfId();
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
// =========== QUALITY MANAGEMENT IMPLEMENTATION ========================
|
||||
// Frames are assumed to come in three types:
|
||||
// Type 1: an AVI key frame or an MPEG I frame.
|
||||
// This frame can be decoded with no history.
|
||||
// Dropping this frame means that no further frame can be decoded
|
||||
// until the next type 1 frame.
|
||||
// Type 1 frames are sync points.
|
||||
// Type 2: an AVI non-key frame or an MPEG P frame.
|
||||
// This frame cannot be decoded unless the previous type 1 frame was
|
||||
// decoded and all type 2 frames since have been decoded.
|
||||
// Dropping this frame means that no further frame can be decoded
|
||||
// until the next type 1 frame.
|
||||
// Type 3: An MPEG B frame.
|
||||
// This frame cannot be decoded unless the previous type 1 or 2 frame
|
||||
// has been decoded AND the subsequent type 1 or 2 frame has also
|
||||
// been decoded. (This requires decoding the frames out of sequence).
|
||||
// Dropping this frame affects no other frames. This implementation
|
||||
// does not allow for these. All non-sync-point frames are treated
|
||||
// as being type 2.
|
||||
//
|
||||
// The spacing of frames of type 1 in a file is not guaranteed. There MUST
|
||||
// be a type 1 frame at (well, near) the start of the file in order to start
|
||||
// decoding at all. After that there could be one every half second or so,
|
||||
// there could be one at the start of each scene (aka "cut", "shot") or
|
||||
// there could be no more at all.
|
||||
// If there is only a single type 1 frame then NO FRAMES CAN BE DROPPED
|
||||
// without losing all the rest of the movie. There is no way to tell whether
|
||||
// this is the case, so we find that we are in the gambling business.
|
||||
// To try to improve the odds, we record the greatest interval between type 1s
|
||||
// that we have seen and we bet on things being no worse than this in the
|
||||
// future.
|
||||
|
||||
// You can tell if it's a type 1 frame by calling IsSyncPoint().
|
||||
// there is no architected way to test for a type 3, so you should override
|
||||
// the quality management here if you have B-frames.
|
||||
|
||||
int m_nKeyFramePeriod; // the largest observed interval between type 1 frames
|
||||
// 1 means every frame is type 1, 2 means every other.
|
||||
|
||||
int m_nFramesSinceKeyFrame; // Used to count frames since the last type 1.
|
||||
// becomes the new m_nKeyFramePeriod if greater.
|
||||
|
||||
BOOL m_bSkipping; // we are skipping to the next type 1 frame
|
||||
|
||||
#ifdef PERF
|
||||
int m_idFrameType; // MSR id Frame type. 1=Key, 2="non-key"
|
||||
int m_idSkip; // MSR id skipping
|
||||
int m_idLate; // MSR id lateness
|
||||
int m_idTimeTillKey; // MSR id for guessed time till next key frame.
|
||||
#endif
|
||||
|
||||
virtual HRESULT StartStreaming();
|
||||
|
||||
HRESULT AbortPlayback(HRESULT hr); // if something bad happens
|
||||
|
||||
HRESULT Receive(IMediaSample *pSample);
|
||||
|
||||
HRESULT AlterQuality(Quality q);
|
||||
|
||||
BOOL ShouldSkipFrame(IMediaSample * pIn);
|
||||
|
||||
int m_itrLate; // lateness from last Quality message
|
||||
// (this overflows at 214 secs late).
|
||||
int m_tDecodeStart; // timeGetTime when decode started.
|
||||
int m_itrAvgDecode; // Average decode time in reference units.
|
||||
|
||||
BOOL m_bNoSkip; // debug - no skipping.
|
||||
|
||||
// We send an EC_QUALITY_CHANGE notification to the app if we have to degrade.
|
||||
// We send one when we start degrading, not one for every frame, this means
|
||||
// we track whether we've sent one yet.
|
||||
BOOL m_bQualityChanged;
|
||||
|
||||
// When non-zero, don't pass anything to renderer until next keyframe
|
||||
// If there are few keys, give up and eventually draw something
|
||||
int m_nWaitForKey;
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -1,393 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: WXDebug.h
|
||||
//
|
||||
// Desc: DirectShow base classes - provides debugging facilities.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __WXDEBUG__
|
||||
#define __WXDEBUG__
|
||||
|
||||
// This library provides fairly straight forward debugging functionality, this
|
||||
// is split into two main sections. The first is assertion handling, there are
|
||||
// three types of assertions provided here. The most commonly used one is the
|
||||
// ASSERT(condition) macro which will pop up a message box including the file
|
||||
// and line number if the condition evaluates to FALSE. Then there is the
|
||||
// EXECUTE_ASSERT macro which is the same as ASSERT except the condition will
|
||||
// still be executed in NON debug builds. The final type of assertion is the
|
||||
// KASSERT macro which is more suitable for pure (perhaps kernel) filters as
|
||||
// the condition is printed onto the debugger rather than in a message box.
|
||||
//
|
||||
// The other part of the debug module facilties is general purpose logging.
|
||||
// This is accessed by calling DbgLog(). The function takes a type and level
|
||||
// field which define the type of informational string you are presenting and
|
||||
// it's relative importance. The type field can be a combination (one or more)
|
||||
// of LOG_TIMING, LOG_TRACE, LOG_MEMORY, LOG_LOCKING and LOG_ERROR. The level
|
||||
// is a DWORD value where zero defines highest important. Use of zero as the
|
||||
// debug logging level is to be encouraged ONLY for major errors or events as
|
||||
// they will ALWAYS be displayed on the debugger. Other debug output has it's
|
||||
// level matched against the current debug output level stored in the registry
|
||||
// for this module and if less than the current setting it will be displayed.
|
||||
//
|
||||
// Each module or executable has it's own debug output level for each of the
|
||||
// five types. These are read in when the DbgInitialise function is called
|
||||
// for DLLs linking to STRMBASE.LIB this is done automatically when the DLL
|
||||
// is loaded, executables must call it explicitely with the module instance
|
||||
// handle given to them through the WINMAIN entry point. An executable must
|
||||
// also call DbgTerminate when they have finished to clean up the resources
|
||||
// the debug library uses, once again this is done automatically for DLLs
|
||||
|
||||
// These are the five different categories of logging information
|
||||
|
||||
enum { LOG_TIMING = 0x01, // Timing and performance measurements
|
||||
LOG_TRACE = 0x02, // General step point call tracing
|
||||
LOG_MEMORY = 0x04, // Memory and object allocation/destruction
|
||||
LOG_LOCKING = 0x08, // Locking/unlocking of critical sections
|
||||
LOG_ERROR = 0x10, // Debug error notification
|
||||
LOG_CUSTOM1 = 0x20,
|
||||
LOG_CUSTOM2 = 0x40,
|
||||
LOG_CUSTOM3 = 0x80,
|
||||
LOG_CUSTOM4 = 0x100,
|
||||
LOG_CUSTOM5 = 0x200,
|
||||
};
|
||||
|
||||
#define LOG_FORCIBLY_SET 0x80000000
|
||||
|
||||
enum { CDISP_HEX = 0x01,
|
||||
CDISP_DEC = 0x02};
|
||||
|
||||
// For each object created derived from CBaseObject (in debug builds) we
|
||||
// create a descriptor that holds it's name (statically allocated memory)
|
||||
// and a cookie we assign it. We keep a list of all the active objects
|
||||
// we have registered so that we can dump a list of remaining objects
|
||||
|
||||
typedef struct tag_ObjectDesc {
|
||||
const CHAR *m_szName;
|
||||
const WCHAR *m_wszName;
|
||||
DWORD m_dwCookie;
|
||||
tag_ObjectDesc *m_pNext;
|
||||
} ObjectDesc;
|
||||
|
||||
#define DLLIMPORT __declspec(dllimport)
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define NAME(x) TEXT(x)
|
||||
|
||||
// These are used internally by the debug library (PRIVATE)
|
||||
|
||||
void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax);
|
||||
void WINAPI DbgInitGlobalSettings(bool fTakeMax);
|
||||
void WINAPI DbgInitModuleSettings(bool fTakeMax);
|
||||
void WINAPI DbgInitModuleName();
|
||||
DWORD WINAPI DbgRegisterObjectCreation(
|
||||
const CHAR *szObjectName, const WCHAR *wszObjectName);
|
||||
|
||||
BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie);
|
||||
|
||||
// These are the PUBLIC entry points
|
||||
|
||||
BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level);
|
||||
void WINAPI DbgSetModuleLevel(DWORD Type,DWORD Level);
|
||||
void WINAPI DbgSetAutoRefreshLevels(bool fAuto);
|
||||
|
||||
// Initialise the library with the module handle
|
||||
|
||||
void WINAPI DbgInitialise(HINSTANCE hInst);
|
||||
void WINAPI DbgTerminate();
|
||||
|
||||
void WINAPI DbgDumpObjectRegister();
|
||||
|
||||
// Display error and logging to the user
|
||||
|
||||
void WINAPI DbgAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine);
|
||||
void WINAPI DbgBreakPoint(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine);
|
||||
void WINAPI DbgBreakPoint(const TCHAR *pFileName,INT iLine,const TCHAR* szFormatString,...);
|
||||
|
||||
void WINAPI DbgKernelAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine);
|
||||
void WINAPI DbgLogInfo(DWORD Type,DWORD Level,const TCHAR *pFormat,...);
|
||||
#ifdef UNICODE
|
||||
void WINAPI DbgLogInfo(DWORD Type,DWORD Level,const CHAR *pFormat,...);
|
||||
void WINAPI DbgAssert(const CHAR *pCondition,const CHAR *pFileName,INT iLine);
|
||||
void WINAPI DbgBreakPoint(const CHAR *pCondition,const CHAR *pFileName,INT iLine);
|
||||
void WINAPI DbgKernelAssert(const CHAR *pCondition,const CHAR *pFileName,INT iLine);
|
||||
#endif
|
||||
void WINAPI DbgOutString(LPCTSTR psz);
|
||||
|
||||
// Debug infinite wait stuff
|
||||
DWORD WINAPI DbgWaitForSingleObject(HANDLE h);
|
||||
DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount,
|
||||
CONST HANDLE *lpHandles,
|
||||
BOOL bWaitAll);
|
||||
void WINAPI DbgSetWaitTimeout(DWORD dwTimeout);
|
||||
|
||||
#ifdef __strmif_h__
|
||||
// Display a media type: Terse at level 2, verbose at level 5
|
||||
void WINAPI DisplayType(LPTSTR label, const AM_MEDIA_TYPE *pmtIn, DWORD dwLevel = 5);
|
||||
|
||||
// Dump lots of information about a filter graph
|
||||
void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel);
|
||||
#endif
|
||||
|
||||
#define KASSERT(_x_) if (!(_x_)) \
|
||||
DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
|
||||
// Break on the debugger without putting up a message box
|
||||
// message goes to debugger instead
|
||||
|
||||
#define KDbgBreak(_x_) \
|
||||
DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
|
||||
// We chose a common name for our ASSERT macro, MFC also uses this name
|
||||
// So long as the implementation evaluates the condition and handles it
|
||||
// then we will be ok. Rather than override the behaviour expected we
|
||||
// will leave whatever first defines ASSERT as the handler (i.e. MFC)
|
||||
#ifndef ASSERT
|
||||
#define ASSERT(_x_) if (!(_x_)) \
|
||||
DbgAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
#endif
|
||||
|
||||
#define DbgAssertAligned( _ptr_, _alignment_ ) ASSERT( ((DWORD_PTR) (_ptr_)) % (_alignment_) == 0)
|
||||
|
||||
// Put up a message box informing the user of a halt
|
||||
// condition in the program
|
||||
|
||||
#define DbgBreak(_x_) \
|
||||
DbgBreakPoint(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
|
||||
#define EXECUTE_ASSERT(_x_) ASSERT(_x_)
|
||||
#define DbgLog(_x_) DbgLogInfo _x_
|
||||
// MFC style trace macros
|
||||
|
||||
#define NOTE(_x_) DbgLog((LOG_TRACE,5,TEXT(_x_)))
|
||||
#define NOTE1(_x_,a) DbgLog((LOG_TRACE,5,TEXT(_x_),a))
|
||||
#define NOTE2(_x_,a,b) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b))
|
||||
#define NOTE3(_x_,a,b,c) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c))
|
||||
#define NOTE4(_x_,a,b,c,d) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d))
|
||||
#define NOTE5(_x_,a,b,c,d,e) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d,e))
|
||||
|
||||
#else
|
||||
|
||||
// Retail builds make public debug functions inert - WARNING the source
|
||||
// files do not define or build any of the entry points in debug builds
|
||||
// (public entry points compile to nothing) so if you go trying to call
|
||||
// any of the private entry points in your source they won't compile
|
||||
|
||||
#define NAME(_x_) ((TCHAR *) NULL)
|
||||
|
||||
#define DbgInitialise(hInst)
|
||||
#define DbgTerminate()
|
||||
#define DbgLog(_x_) 0
|
||||
#define DbgOutString(psz)
|
||||
#define DbgAssertAligned( _ptr_, _alignment_ ) 0
|
||||
|
||||
#define DbgRegisterObjectCreation(pObjectName)
|
||||
#define DbgRegisterObjectDestruction(dwCookie)
|
||||
#define DbgDumpObjectRegister()
|
||||
|
||||
#define DbgCheckModuleLevel(Type,Level)
|
||||
#define DbgSetModuleLevel(Type,Level)
|
||||
#define DbgSetAutoRefreshLevels(fAuto)
|
||||
|
||||
#define DbgWaitForSingleObject(h) WaitForSingleObject(h, INFINITE)
|
||||
#define DbgWaitForMultipleObjects(nCount, lpHandles, bWaitAll) \
|
||||
WaitForMultipleObjects(nCount, lpHandles, bWaitAll, INFINITE)
|
||||
#define DbgSetWaitTimeout(dwTimeout)
|
||||
|
||||
#define KDbgBreak(_x_)
|
||||
#define DbgBreak(_x_)
|
||||
|
||||
#define KASSERT(_x_) ((void)0)
|
||||
#ifndef ASSERT
|
||||
#define ASSERT(_x_) ((void)0)
|
||||
#endif
|
||||
#define EXECUTE_ASSERT(_x_) ((void)(_x_))
|
||||
|
||||
// MFC style trace macros
|
||||
|
||||
#define NOTE(_x_) ((void)0)
|
||||
#define NOTE1(_x_,a) ((void)0)
|
||||
#define NOTE2(_x_,a,b) ((void)0)
|
||||
#define NOTE3(_x_,a,b,c) ((void)0)
|
||||
#define NOTE4(_x_,a,b,c,d) ((void)0)
|
||||
#define NOTE5(_x_,a,b,c,d,e) ((void)0)
|
||||
|
||||
#define DisplayType(label, pmtIn) ((void)0)
|
||||
#define DumpGraph(pGraph, label) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
// Checks a pointer which should be non NULL - can be used as follows.
|
||||
|
||||
#define CheckPointer(p,ret) {if((p)==NULL) return (ret);}
|
||||
|
||||
// HRESULT Foo(VOID *pBar)
|
||||
// {
|
||||
// CheckPointer(pBar,E_INVALIDARG)
|
||||
// }
|
||||
//
|
||||
// Or if the function returns a boolean
|
||||
//
|
||||
// BOOL Foo(VOID *pBar)
|
||||
// {
|
||||
// CheckPointer(pBar,FALSE)
|
||||
// }
|
||||
|
||||
// These validate pointers when symbol VFWROBUST is defined
|
||||
// This will normally be defined in debug not retail builds
|
||||
|
||||
#ifdef DEBUG
|
||||
#define VFWROBUST
|
||||
#endif
|
||||
|
||||
#ifdef VFWROBUST
|
||||
|
||||
#define ValidateReadPtr(p,cb) \
|
||||
{if(IsBadReadPtr((PVOID)p,cb) == TRUE) \
|
||||
DbgBreak("Invalid read pointer");}
|
||||
|
||||
#define ValidateWritePtr(p,cb) \
|
||||
{if(IsBadWritePtr((PVOID)p,cb) == TRUE) \
|
||||
DbgBreak("Invalid write pointer");}
|
||||
|
||||
#define ValidateReadWritePtr(p,cb) \
|
||||
{ValidateReadPtr(p,cb) ValidateWritePtr(p,cb)}
|
||||
|
||||
#define ValidateStringPtr(p) \
|
||||
{if(IsBadStringPtr((LPCTSTR)p,INFINITE) == TRUE) \
|
||||
DbgBreak("Invalid string pointer");}
|
||||
|
||||
#define ValidateStringPtrA(p) \
|
||||
{if(IsBadStringPtrA((LPCSTR)p,INFINITE) == TRUE) \
|
||||
DbgBreak("Invalid ANSI string pointer");}
|
||||
|
||||
#define ValidateStringPtrW(p) \
|
||||
{if(IsBadStringPtrW((LPCWSTR)p,INFINITE) == TRUE) \
|
||||
DbgBreak("Invalid UNICODE string pointer");}
|
||||
|
||||
#else
|
||||
#define ValidateReadPtr(p,cb) 0
|
||||
#define ValidateWritePtr(p,cb) 0
|
||||
#define ValidateReadWritePtr(p,cb) 0
|
||||
#define ValidateStringPtr(p) 0
|
||||
#define ValidateStringPtrA(p) 0
|
||||
#define ValidateStringPtrW(p) 0
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _OBJBASE_H_
|
||||
|
||||
// Outputting GUID names. If you want to include the name
|
||||
// associated with a GUID (eg CLSID_...) then
|
||||
//
|
||||
// GuidNames[yourGUID]
|
||||
//
|
||||
// Returns the name defined in uuids.h as a string
|
||||
|
||||
typedef struct {
|
||||
CHAR *szName;
|
||||
GUID guid;
|
||||
} GUID_STRING_ENTRY;
|
||||
|
||||
class CGuidNameList {
|
||||
public:
|
||||
CHAR *operator [] (const GUID& guid);
|
||||
};
|
||||
|
||||
extern CGuidNameList GuidNames;
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef REMIND
|
||||
// REMIND macro - generates warning as reminder to complete coding
|
||||
// (eg) usage:
|
||||
//
|
||||
// #pragma message (REMIND("Add automation support"))
|
||||
|
||||
|
||||
#define QUOTE(x) #x
|
||||
#define QQUOTE(y) QUOTE(y)
|
||||
#define REMIND(str) __FILE__ "(" QQUOTE(__LINE__) ") : " str
|
||||
#endif
|
||||
|
||||
// Method to display objects in a useful format
|
||||
//
|
||||
// eg If you want to display a LONGLONG ll in a debug string do (eg)
|
||||
//
|
||||
// DbgLog((LOG_TRACE, n, TEXT("Value is %s"), (LPCTSTR)CDisp(ll, CDISP_HEX)));
|
||||
|
||||
|
||||
class CDispBasic
|
||||
{
|
||||
public:
|
||||
CDispBasic() { m_pString = m_String; };
|
||||
~CDispBasic();
|
||||
protected:
|
||||
TCHAR* m_pString; // normally points to m_String... unless too much data
|
||||
TCHAR m_String[50];
|
||||
};
|
||||
class CDisp : public CDispBasic
|
||||
{
|
||||
public:
|
||||
CDisp(LONGLONG ll, int Format = CDISP_HEX); // Display a LONGLONG in CDISP_HEX or CDISP_DEC form
|
||||
CDisp(REFCLSID clsid); // Display a GUID
|
||||
CDisp(double d); // Display a floating point number
|
||||
#ifdef __strmif_h__
|
||||
#ifdef __STREAMS__
|
||||
CDisp(CRefTime t); // Display a Reference Time
|
||||
#endif
|
||||
CDisp(IPin *pPin); // Display a pin as {filter clsid}(pin name)
|
||||
CDisp(IUnknown *pUnk); // Display a filter or pin
|
||||
#endif // __strmif_h__
|
||||
~CDisp();
|
||||
|
||||
// Implement cast to (LPCTSTR) as parameter to logger
|
||||
operator LPCTSTR()
|
||||
{
|
||||
return (LPCTSTR)m_pString;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#if defined(DEBUG)
|
||||
class CAutoTrace
|
||||
{
|
||||
private:
|
||||
const TCHAR* _szBlkName;
|
||||
const int _level;
|
||||
static const TCHAR _szEntering[];
|
||||
static const TCHAR _szLeaving[];
|
||||
public:
|
||||
CAutoTrace(const TCHAR* szBlkName, const int level = 15)
|
||||
: _szBlkName(szBlkName), _level(level)
|
||||
{DbgLog((LOG_TRACE, _level, _szEntering, _szBlkName));}
|
||||
|
||||
~CAutoTrace()
|
||||
{DbgLog((LOG_TRACE, _level, _szLeaving, _szBlkName));}
|
||||
};
|
||||
|
||||
#if defined (__FUNCTION__)
|
||||
|
||||
#define AMTRACEFN() CAutoTrace __trace(TEXT(__FUNCTION__))
|
||||
#define AMTRACE(_x_) CAutoTrace __trace(TEXT(__FUNCTION__))
|
||||
|
||||
#else
|
||||
|
||||
#define AMTRACE(_x_) CAutoTrace __trace _x_
|
||||
#define AMTRACEFN()
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#define AMTRACE(_x_)
|
||||
#define AMTRACEFN()
|
||||
|
||||
#endif
|
||||
|
||||
#endif // __WXDEBUG__
|
||||
|
||||
|
@ -1,885 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: WXList.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements a non-MFC based generic list
|
||||
// template class.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/* A generic list of pointers to objects.
|
||||
Objectives: avoid using MFC libraries in ndm kernel mode and
|
||||
provide a really useful list type.
|
||||
|
||||
The class is thread safe in that separate threads may add and
|
||||
delete items in the list concurrently although the application
|
||||
must ensure that constructor and destructor access is suitably
|
||||
synchronised.
|
||||
|
||||
The list name must not conflict with MFC classes as an
|
||||
application may use both
|
||||
|
||||
The nodes form a doubly linked, NULL terminated chain with an anchor
|
||||
block (the list object per se) holding pointers to the first and last
|
||||
nodes and a count of the nodes.
|
||||
There is a node cache to reduce the allocation and freeing overhead.
|
||||
It optionally (determined at construction time) has an Event which is
|
||||
set whenever the list becomes non-empty and reset whenever it becomes
|
||||
empty.
|
||||
It optionally (determined at construction time) has a Critical Section
|
||||
which is entered during the important part of each operation. (About
|
||||
all you can do outside it is some parameter checking).
|
||||
|
||||
The node cache is a repository of nodes that are NOT in the list to speed
|
||||
up storage allocation. Each list has its own cache to reduce locking and
|
||||
serialising. The list accesses are serialised anyway for a given list - a
|
||||
common cache would mean that we would have to separately serialise access
|
||||
of all lists within the cache. Because the cache only stores nodes that are
|
||||
not in the list, releasing the cache does not release any list nodes. This
|
||||
means that list nodes can be copied or rechained from one list to another
|
||||
without danger of creating a dangling reference if the original cache goes
|
||||
away.
|
||||
|
||||
Questionable design decisions:
|
||||
1. Retaining the warts for compatibility
|
||||
2. Keeping an element count -i.e. counting whenever we do anything
|
||||
instead of only when we want the count.
|
||||
3. Making the chain pointers NULL terminated. If the list object
|
||||
itself looks just like a node and the list is kept as a ring then
|
||||
it reduces the number of special cases. All inserts look the same.
|
||||
*/
|
||||
|
||||
|
||||
#include "streams.h"
|
||||
|
||||
/* set cursor to the position of each element of list in turn */
|
||||
#define INTERNALTRAVERSELIST(list, cursor) \
|
||||
for ( cursor = (list).GetHeadPositionI() \
|
||||
; cursor!=NULL \
|
||||
; cursor = (list).Next(cursor) \
|
||||
)
|
||||
|
||||
|
||||
/* set cursor to the position of each element of list in turn
|
||||
in reverse order
|
||||
*/
|
||||
#define INTERNALREVERSETRAVERSELIST(list, cursor) \
|
||||
for ( cursor = (list).GetTailPositionI() \
|
||||
; cursor!=NULL \
|
||||
; cursor = (list).Prev(cursor) \
|
||||
)
|
||||
|
||||
/* Constructor calls a separate initialisation function that
|
||||
creates a node cache, optionally creates a lock object
|
||||
and optionally creates a signaling object.
|
||||
|
||||
By default we create a locking object, a DEFAULTCACHE sized
|
||||
cache but no event object so the list cannot be used in calls
|
||||
to WaitForSingleObject
|
||||
*/
|
||||
CBaseList::CBaseList(TCHAR *pName, // Descriptive list name
|
||||
INT iItems) : // Node cache size
|
||||
#ifdef DEBUG
|
||||
CBaseObject(pName),
|
||||
#endif
|
||||
m_pFirst(NULL),
|
||||
m_pLast(NULL),
|
||||
m_Count(0),
|
||||
m_Cache(iItems)
|
||||
{
|
||||
} // constructor
|
||||
|
||||
CBaseList::CBaseList(TCHAR *pName) : // Descriptive list name
|
||||
#ifdef DEBUG
|
||||
CBaseObject(pName),
|
||||
#endif
|
||||
m_pFirst(NULL),
|
||||
m_pLast(NULL),
|
||||
m_Count(0),
|
||||
m_Cache(DEFAULTCACHE)
|
||||
{
|
||||
} // constructor
|
||||
|
||||
#ifdef UNICODE
|
||||
CBaseList::CBaseList(CHAR *pName, // Descriptive list name
|
||||
INT iItems) : // Node cache size
|
||||
#ifdef DEBUG
|
||||
CBaseObject(pName),
|
||||
#endif
|
||||
m_pFirst(NULL),
|
||||
m_pLast(NULL),
|
||||
m_Count(0),
|
||||
m_Cache(iItems)
|
||||
{
|
||||
} // constructor
|
||||
|
||||
CBaseList::CBaseList(CHAR *pName) : // Descriptive list name
|
||||
#ifdef DEBUG
|
||||
CBaseObject(pName),
|
||||
#endif
|
||||
m_pFirst(NULL),
|
||||
m_pLast(NULL),
|
||||
m_Count(0),
|
||||
m_Cache(DEFAULTCACHE)
|
||||
{
|
||||
} // constructor
|
||||
|
||||
#endif
|
||||
|
||||
/* The destructor enumerates all the node objects in the list and
|
||||
in the cache deleting each in turn. We do not do any processing
|
||||
on the objects that the list holds (i.e. points to) so if they
|
||||
represent interfaces for example the creator of the list should
|
||||
ensure that each of them is released before deleting us
|
||||
*/
|
||||
CBaseList::~CBaseList()
|
||||
{
|
||||
/* Delete all our list nodes */
|
||||
|
||||
RemoveAll();
|
||||
|
||||
} // destructor
|
||||
|
||||
/* Remove all the nodes from the list but don't do anything
|
||||
with the objects that each node looks after (this is the
|
||||
responsibility of the creator).
|
||||
Aa a last act we reset the signalling event
|
||||
(if available) to indicate to clients that the list
|
||||
does not have any entries in it.
|
||||
*/
|
||||
void CBaseList::RemoveAll()
|
||||
{
|
||||
/* Free up all the CNode objects NOTE we don't bother putting the
|
||||
deleted nodes into the cache as this method is only really called
|
||||
in serious times of change such as when we are being deleted at
|
||||
which point the cache will be deleted anway */
|
||||
|
||||
CNode *pn = m_pFirst;
|
||||
while (pn) {
|
||||
CNode *op = pn;
|
||||
pn = pn->Next();
|
||||
delete op;
|
||||
}
|
||||
|
||||
/* Reset the object count and the list pointers */
|
||||
|
||||
m_Count = 0;
|
||||
m_pFirst = m_pLast = NULL;
|
||||
|
||||
} // RemoveAll
|
||||
|
||||
|
||||
|
||||
/* Return a position enumerator for the entire list.
|
||||
A position enumerator is a pointer to a node object cast to a
|
||||
transparent type so all we do is return the head/tail node
|
||||
pointer in the list.
|
||||
WARNING because the position is a pointer to a node there is
|
||||
an implicit assumption for users a the list class that after
|
||||
deleting an object from the list that any other position
|
||||
enumerators that you have may be invalid (since the node
|
||||
may be gone).
|
||||
*/
|
||||
WXLIST_POSITION CBaseList::GetHeadPositionI() const
|
||||
{
|
||||
return (WXLIST_POSITION) m_pFirst;
|
||||
} // GetHeadPosition
|
||||
|
||||
|
||||
|
||||
WXLIST_POSITION CBaseList::GetTailPositionI() const
|
||||
{
|
||||
return (WXLIST_POSITION) m_pLast;
|
||||
} // GetTailPosition
|
||||
|
||||
|
||||
|
||||
/* Get the number of objects in the list,
|
||||
Get the lock before accessing the count.
|
||||
Locking may not be entirely necessary but it has the side effect
|
||||
of making sure that all operations are complete before we get it.
|
||||
So for example if a list is being added to this list then that
|
||||
will have completed in full before we continue rather than seeing
|
||||
an intermediate albeit valid state
|
||||
*/
|
||||
int CBaseList::GetCountI() const
|
||||
{
|
||||
return m_Count;
|
||||
} // GetCount
|
||||
|
||||
|
||||
|
||||
/* Return the object at rp, update rp to the next object from
|
||||
the list or NULL if you have moved over the last object.
|
||||
You may still call this function once we return NULL but
|
||||
we will continue to return a NULL position value
|
||||
*/
|
||||
void *CBaseList::GetNextI(WXLIST_POSITION& rp) const
|
||||
{
|
||||
/* have we reached the end of the list */
|
||||
|
||||
if (rp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lock the object before continuing */
|
||||
|
||||
void *pObject;
|
||||
|
||||
/* Copy the original position then step on */
|
||||
|
||||
CNode *pn = (CNode *) rp;
|
||||
ASSERT(pn != NULL);
|
||||
rp = (WXLIST_POSITION) pn->Next();
|
||||
|
||||
/* Get the object at the original position from the list */
|
||||
|
||||
pObject = pn->GetData();
|
||||
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
|
||||
return pObject;
|
||||
} //GetNext
|
||||
|
||||
|
||||
|
||||
/* Return the object at p.
|
||||
Asking for the object at NULL ASSERTs then returns NULL
|
||||
The object is NOT locked. The list is not being changed
|
||||
in any way. If another thread is busy deleting the object
|
||||
then locking would only result in a change from one bad
|
||||
behaviour to another.
|
||||
*/
|
||||
void *CBaseList::GetI(WXLIST_POSITION p) const
|
||||
{
|
||||
if (p == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CNode * pn = (CNode *) p;
|
||||
void *pObject = pn->GetData();
|
||||
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
|
||||
return pObject;
|
||||
} //Get
|
||||
|
||||
|
||||
|
||||
/* Return the first position in the list which holds the given pointer.
|
||||
Return NULL if it's not found.
|
||||
*/
|
||||
WXLIST_POSITION CBaseList::FindI( void * pObj) const
|
||||
{
|
||||
WXLIST_POSITION pn;
|
||||
INTERNALTRAVERSELIST(*this, pn){
|
||||
if (GetI(pn)==pObj) {
|
||||
return pn;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
} // Find
|
||||
|
||||
|
||||
|
||||
/* Remove the first node in the list (deletes the pointer to its object
|
||||
from the list, does not free the object itself).
|
||||
Return the pointer to its object or NULL if empty
|
||||
*/
|
||||
void *CBaseList::RemoveHeadI()
|
||||
{
|
||||
/* All we do is get the head position and ask for that to be deleted.
|
||||
We could special case this since some of the code path checking
|
||||
in Remove() is redundant as we know there is no previous
|
||||
node for example but it seems to gain little over the
|
||||
added complexity
|
||||
*/
|
||||
|
||||
return RemoveI((WXLIST_POSITION)m_pFirst);
|
||||
} // RemoveHead
|
||||
|
||||
|
||||
|
||||
/* Remove the last node in the list (deletes the pointer to its object
|
||||
from the list, does not free the object itself).
|
||||
Return the pointer to its object or NULL if empty
|
||||
*/
|
||||
void *CBaseList::RemoveTailI()
|
||||
{
|
||||
/* All we do is get the tail position and ask for that to be deleted.
|
||||
We could special case this since some of the code path checking
|
||||
in Remove() is redundant as we know there is no previous
|
||||
node for example but it seems to gain little over the
|
||||
added complexity
|
||||
*/
|
||||
|
||||
return RemoveI((WXLIST_POSITION)m_pLast);
|
||||
} // RemoveTail
|
||||
|
||||
|
||||
|
||||
/* Remove the pointer to the object in this position from the list.
|
||||
Deal with all the chain pointers
|
||||
Return a pointer to the object removed from the list.
|
||||
The node object that is freed as a result
|
||||
of this operation is added to the node cache where
|
||||
it can be used again.
|
||||
Remove(NULL) is a harmless no-op - but probably is a wart.
|
||||
*/
|
||||
void *CBaseList::RemoveI(WXLIST_POSITION pos)
|
||||
{
|
||||
/* Lock the critical section before continuing */
|
||||
|
||||
// ASSERT (pos!=NULL); // Removing NULL is to be harmless!
|
||||
if (pos==NULL) return NULL;
|
||||
|
||||
|
||||
CNode *pCurrent = (CNode *) pos;
|
||||
ASSERT(pCurrent != NULL);
|
||||
|
||||
/* Update the previous node */
|
||||
|
||||
CNode *pNode = pCurrent->Prev();
|
||||
if (pNode == NULL) {
|
||||
m_pFirst = pCurrent->Next();
|
||||
} else {
|
||||
pNode->SetNext(pCurrent->Next());
|
||||
}
|
||||
|
||||
/* Update the following node */
|
||||
|
||||
pNode = pCurrent->Next();
|
||||
if (pNode == NULL) {
|
||||
m_pLast = pCurrent->Prev();
|
||||
} else {
|
||||
pNode->SetPrev(pCurrent->Prev());
|
||||
}
|
||||
|
||||
/* Get the object this node was looking after */
|
||||
|
||||
void *pObject = pCurrent->GetData();
|
||||
|
||||
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
|
||||
|
||||
/* Try and add the node object to the cache -
|
||||
a NULL return code from the cache means we ran out of room.
|
||||
The cache size is fixed by a constructor argument when the
|
||||
list is created and defaults to DEFAULTCACHE.
|
||||
This means that the cache will have room for this many
|
||||
node objects. So if you have a list of media samples
|
||||
and you know there will never be more than five active at
|
||||
any given time of them for example then override the default
|
||||
constructor
|
||||
*/
|
||||
|
||||
m_Cache.AddToCache(pCurrent);
|
||||
|
||||
/* If the list is empty then reset the list event */
|
||||
|
||||
--m_Count;
|
||||
ASSERT(m_Count >= 0);
|
||||
return pObject;
|
||||
} // Remove
|
||||
|
||||
|
||||
|
||||
/* Add this object to the tail end of our list
|
||||
Return the new tail position.
|
||||
*/
|
||||
|
||||
WXLIST_POSITION CBaseList::AddTailI(void *pObject)
|
||||
{
|
||||
/* Lock the critical section before continuing */
|
||||
|
||||
CNode *pNode;
|
||||
// ASSERT(pObject); // NULL pointers in the list are allowed.
|
||||
|
||||
/* If there is a node objects in the cache then use
|
||||
that otherwise we will have to create a new one */
|
||||
|
||||
pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObject);
|
||||
pNode->SetNext(NULL);
|
||||
pNode->SetPrev(m_pLast);
|
||||
|
||||
if (m_pLast == NULL) {
|
||||
m_pFirst = pNode;
|
||||
} else {
|
||||
m_pLast->SetNext(pNode);
|
||||
}
|
||||
|
||||
/* Set the new last node pointer and also increment the number
|
||||
of list entries, the critical section is unlocked when we
|
||||
exit the function
|
||||
*/
|
||||
|
||||
m_pLast = pNode;
|
||||
++m_Count;
|
||||
|
||||
return (WXLIST_POSITION) pNode;
|
||||
} // AddTail(object)
|
||||
|
||||
|
||||
|
||||
/* Add this object to the head end of our list
|
||||
Return the new head position.
|
||||
*/
|
||||
WXLIST_POSITION CBaseList::AddHeadI(void *pObject)
|
||||
{
|
||||
CNode *pNode;
|
||||
// ASSERT(pObject); // NULL pointers in the list are allowed.
|
||||
|
||||
/* If there is a node objects in the cache then use
|
||||
that otherwise we will have to create a new one */
|
||||
|
||||
pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObject);
|
||||
|
||||
/* chain it in (set four pointers) */
|
||||
pNode->SetPrev(NULL);
|
||||
pNode->SetNext(m_pFirst);
|
||||
|
||||
if (m_pFirst == NULL) {
|
||||
m_pLast = pNode;
|
||||
} else {
|
||||
m_pFirst->SetPrev(pNode);
|
||||
}
|
||||
m_pFirst = pNode;
|
||||
|
||||
++m_Count;
|
||||
|
||||
return (WXLIST_POSITION) pNode;
|
||||
} // AddHead(object)
|
||||
|
||||
|
||||
|
||||
/* Add all the elements in *pList to the tail of this list.
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails some elements may have been added.
|
||||
*/
|
||||
BOOL CBaseList::AddTail(CBaseList *pList)
|
||||
{
|
||||
/* lock the object before starting then enumerate
|
||||
each entry in the source list and add them one by one to
|
||||
our list (while still holding the object lock)
|
||||
Lock the other list too.
|
||||
*/
|
||||
WXLIST_POSITION pos = pList->GetHeadPositionI();
|
||||
|
||||
while (pos) {
|
||||
if (NULL == AddTailI(pList->GetNextI(pos))) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
} // AddTail(list)
|
||||
|
||||
|
||||
|
||||
/* Add all the elements in *pList to the head of this list.
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails some elements may have been added.
|
||||
*/
|
||||
BOOL CBaseList::AddHead(CBaseList *pList)
|
||||
{
|
||||
/* lock the object before starting then enumerate
|
||||
each entry in the source list and add them one by one to
|
||||
our list (while still holding the object lock)
|
||||
Lock the other list too.
|
||||
|
||||
To avoid reversing the list, traverse it backwards.
|
||||
*/
|
||||
|
||||
WXLIST_POSITION pos;
|
||||
|
||||
INTERNALREVERSETRAVERSELIST(*pList, pos) {
|
||||
if (NULL== AddHeadI(pList->GetI(pos))){
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
} // AddHead(list)
|
||||
|
||||
|
||||
|
||||
/* Add the object after position p
|
||||
p is still valid after the operation.
|
||||
AddAfter(NULL,x) adds x to the start - same as AddHead
|
||||
Return the position of the new object, NULL if it failed
|
||||
*/
|
||||
WXLIST_POSITION CBaseList::AddAfterI(WXLIST_POSITION pos, void * pObj)
|
||||
{
|
||||
if (pos==NULL)
|
||||
return AddHeadI(pObj);
|
||||
|
||||
/* As someone else might be furkling with the list -
|
||||
Lock the critical section before continuing
|
||||
*/
|
||||
CNode *pAfter = (CNode *) pos;
|
||||
ASSERT(pAfter != NULL);
|
||||
if (pAfter==m_pLast)
|
||||
return AddTailI(pObj);
|
||||
|
||||
/* set pnode to point to a new node, preferably from the cache */
|
||||
|
||||
CNode *pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObj);
|
||||
|
||||
/* It is to be added to the middle of the list - there is a before
|
||||
and after node. Chain it after pAfter, before pBefore.
|
||||
*/
|
||||
CNode * pBefore = pAfter->Next();
|
||||
ASSERT(pBefore != NULL);
|
||||
|
||||
/* chain it in (set four pointers) */
|
||||
pNode->SetPrev(pAfter);
|
||||
pNode->SetNext(pBefore);
|
||||
pBefore->SetPrev(pNode);
|
||||
pAfter->SetNext(pNode);
|
||||
|
||||
++m_Count;
|
||||
|
||||
return (WXLIST_POSITION) pNode;
|
||||
|
||||
} // AddAfter(object)
|
||||
|
||||
|
||||
|
||||
BOOL CBaseList::AddAfter(WXLIST_POSITION p, CBaseList *pList)
|
||||
{
|
||||
WXLIST_POSITION pos;
|
||||
INTERNALTRAVERSELIST(*pList, pos) {
|
||||
/* p follows along the elements being added */
|
||||
p = AddAfterI(p, pList->GetI(pos));
|
||||
if (p==NULL) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
} // AddAfter(list)
|
||||
|
||||
|
||||
|
||||
/* Mirror images:
|
||||
Add the element or list after position p.
|
||||
p is still valid after the operation.
|
||||
AddBefore(NULL,x) adds x to the end - same as AddTail
|
||||
*/
|
||||
WXLIST_POSITION CBaseList::AddBeforeI(WXLIST_POSITION pos, void * pObj)
|
||||
{
|
||||
if (pos==NULL)
|
||||
return AddTailI(pObj);
|
||||
|
||||
/* set pnode to point to a new node, preferably from the cache */
|
||||
|
||||
CNode *pBefore = (CNode *) pos;
|
||||
ASSERT(pBefore != NULL);
|
||||
if (pBefore==m_pFirst)
|
||||
return AddHeadI(pObj);
|
||||
|
||||
CNode * pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObj);
|
||||
|
||||
/* It is to be added to the middle of the list - there is a before
|
||||
and after node. Chain it after pAfter, before pBefore.
|
||||
*/
|
||||
|
||||
CNode * pAfter = pBefore->Prev();
|
||||
ASSERT(pAfter != NULL);
|
||||
|
||||
/* chain it in (set four pointers) */
|
||||
pNode->SetPrev(pAfter);
|
||||
pNode->SetNext(pBefore);
|
||||
pBefore->SetPrev(pNode);
|
||||
pAfter->SetNext(pNode);
|
||||
|
||||
++m_Count;
|
||||
|
||||
return (WXLIST_POSITION) pNode;
|
||||
|
||||
} // Addbefore(object)
|
||||
|
||||
|
||||
|
||||
BOOL CBaseList::AddBefore(WXLIST_POSITION p, CBaseList *pList)
|
||||
{
|
||||
WXLIST_POSITION pos;
|
||||
INTERNALREVERSETRAVERSELIST(*pList, pos) {
|
||||
/* p follows along the elements being added */
|
||||
p = AddBeforeI(p, pList->GetI(pos));
|
||||
if (p==NULL) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
} // AddBefore(list)
|
||||
|
||||
|
||||
|
||||
/* Split *this after position p in *this
|
||||
Retain as *this the tail portion of the original *this
|
||||
Add the head portion to the tail end of *pList
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
|
||||
e.g.
|
||||
foo->MoveToTail(foo->GetHeadPosition(), bar);
|
||||
moves one element from the head of foo to the tail of bar
|
||||
foo->MoveToTail(NULL, bar);
|
||||
is a no-op
|
||||
foo->MoveToTail(foo->GetTailPosition, bar);
|
||||
concatenates foo onto the end of bar and empties foo.
|
||||
|
||||
A better, except excessively long name might be
|
||||
MoveElementsFromHeadThroughPositionToOtherTail
|
||||
*/
|
||||
BOOL CBaseList::MoveToTail
|
||||
(WXLIST_POSITION pos, CBaseList *pList)
|
||||
{
|
||||
/* Algorithm:
|
||||
Note that the elements (including their order) in the concatenation
|
||||
of *pList to the head of *this is invariant.
|
||||
1. Count elements to be moved
|
||||
2. Join *pList onto the head of this to make one long chain
|
||||
3. Set first/Last pointers in *this and *pList
|
||||
4. Break the chain at the new place
|
||||
5. Adjust counts
|
||||
6. Set/Reset any events
|
||||
*/
|
||||
|
||||
if (pos==NULL) return TRUE; // no-op. Eliminates special cases later.
|
||||
|
||||
|
||||
/* Make cMove the number of nodes to move */
|
||||
CNode * p = (CNode *)pos;
|
||||
int cMove = 0; // number of nodes to move
|
||||
while(p!=NULL) {
|
||||
p = p->Prev();
|
||||
++cMove;
|
||||
}
|
||||
|
||||
|
||||
/* Join the two chains together */
|
||||
if (pList->m_pLast!=NULL)
|
||||
pList->m_pLast->SetNext(m_pFirst);
|
||||
if (m_pFirst!=NULL)
|
||||
m_pFirst->SetPrev(pList->m_pLast);
|
||||
|
||||
|
||||
/* set first and last pointers */
|
||||
p = (CNode *)pos;
|
||||
|
||||
if (pList->m_pFirst==NULL)
|
||||
pList->m_pFirst = m_pFirst;
|
||||
m_pFirst = p->Next();
|
||||
if (m_pFirst==NULL)
|
||||
m_pLast = NULL;
|
||||
pList->m_pLast = p;
|
||||
|
||||
|
||||
/* Break the chain after p to create the new pieces */
|
||||
if (m_pFirst!=NULL)
|
||||
m_pFirst->SetPrev(NULL);
|
||||
p->SetNext(NULL);
|
||||
|
||||
|
||||
/* Adjust the counts */
|
||||
m_Count -= cMove;
|
||||
pList->m_Count += cMove;
|
||||
|
||||
return TRUE;
|
||||
|
||||
} // MoveToTail
|
||||
|
||||
|
||||
|
||||
/* Mirror image of MoveToTail:
|
||||
Split *this before position p in *this.
|
||||
Retain in *this the head portion of the original *this
|
||||
Add the tail portion to the start (i.e. head) of *pList
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
|
||||
e.g.
|
||||
foo->MoveToHead(foo->GetTailPosition(), bar);
|
||||
moves one element from the tail of foo to the head of bar
|
||||
foo->MoveToHead(NULL, bar);
|
||||
is a no-op
|
||||
foo->MoveToHead(foo->GetHeadPosition, bar);
|
||||
concatenates foo onto the start of bar and empties foo.
|
||||
*/
|
||||
BOOL CBaseList::MoveToHead
|
||||
(WXLIST_POSITION pos, CBaseList *pList)
|
||||
{
|
||||
|
||||
/* See the comments on the algorithm in MoveToTail */
|
||||
|
||||
if (pos==NULL) return TRUE; // no-op. Eliminates special cases later.
|
||||
|
||||
/* Make cMove the number of nodes to move */
|
||||
CNode * p = (CNode *)pos;
|
||||
int cMove = 0; // number of nodes to move
|
||||
while(p!=NULL) {
|
||||
p = p->Next();
|
||||
++cMove;
|
||||
}
|
||||
|
||||
|
||||
/* Join the two chains together */
|
||||
if (pList->m_pFirst!=NULL)
|
||||
pList->m_pFirst->SetPrev(m_pLast);
|
||||
if (m_pLast!=NULL)
|
||||
m_pLast->SetNext(pList->m_pFirst);
|
||||
|
||||
|
||||
/* set first and last pointers */
|
||||
p = (CNode *)pos;
|
||||
|
||||
|
||||
if (pList->m_pLast==NULL)
|
||||
pList->m_pLast = m_pLast;
|
||||
|
||||
m_pLast = p->Prev();
|
||||
if (m_pLast==NULL)
|
||||
m_pFirst = NULL;
|
||||
pList->m_pFirst = p;
|
||||
|
||||
|
||||
/* Break the chain after p to create the new pieces */
|
||||
if (m_pLast!=NULL)
|
||||
m_pLast->SetNext(NULL);
|
||||
p->SetPrev(NULL);
|
||||
|
||||
|
||||
/* Adjust the counts */
|
||||
m_Count -= cMove;
|
||||
pList->m_Count += cMove;
|
||||
|
||||
return TRUE;
|
||||
|
||||
} // MoveToHead
|
||||
|
||||
|
||||
|
||||
/* Reverse the order of the [pointers to] objects in *this
|
||||
*/
|
||||
void CBaseList::Reverse()
|
||||
{
|
||||
/* algorithm:
|
||||
The obvious booby trap is that you flip pointers around and lose
|
||||
addressability to the node that you are going to process next.
|
||||
The easy way to avoid this is do do one chain at a time.
|
||||
|
||||
Run along the forward chain,
|
||||
For each node, set the reverse pointer to the one ahead of us.
|
||||
The reverse chain is now a copy of the old forward chain, including
|
||||
the NULL termination.
|
||||
|
||||
Run along the reverse chain (i.e. old forward chain again)
|
||||
For each node set the forward pointer of the node ahead to point back
|
||||
to the one we're standing on.
|
||||
The first node needs special treatment,
|
||||
it's new forward pointer is NULL.
|
||||
Finally set the First/Last pointers
|
||||
|
||||
*/
|
||||
CNode * p;
|
||||
|
||||
// Yes we COULD use a traverse, but it would look funny!
|
||||
p = m_pFirst;
|
||||
while (p!=NULL) {
|
||||
CNode * q;
|
||||
q = p->Next();
|
||||
p->SetNext(p->Prev());
|
||||
p->SetPrev(q);
|
||||
p = q;
|
||||
}
|
||||
|
||||
p = m_pFirst;
|
||||
m_pFirst = m_pLast;
|
||||
m_pLast = p;
|
||||
|
||||
|
||||
#if 0 // old version
|
||||
|
||||
if (m_pFirst==NULL) return; // empty list
|
||||
if (m_pFirst->Next()==NULL) return; // single node list
|
||||
|
||||
|
||||
/* run along forward chain */
|
||||
for ( p = m_pFirst
|
||||
; p!=NULL
|
||||
; p = p->Next()
|
||||
){
|
||||
p->SetPrev(p->Next());
|
||||
}
|
||||
|
||||
|
||||
/* special case first element */
|
||||
m_pFirst->SetNext(NULL); // fix the old first element
|
||||
|
||||
|
||||
/* run along new reverse chain i.e. old forward chain again */
|
||||
for ( p = m_pFirst // start at the old first element
|
||||
; p->Prev()!=NULL // while there's a node still to be set
|
||||
; p = p->Prev() // work in the same direction as before
|
||||
){
|
||||
p->Prev()->SetNext(p);
|
||||
}
|
||||
|
||||
|
||||
/* fix forward and reverse pointers
|
||||
- the triple XOR swap would work but all the casts look hideous */
|
||||
p = m_pFirst;
|
||||
m_pFirst = m_pLast;
|
||||
m_pLast = p;
|
||||
#endif
|
||||
|
||||
} // Reverse
|
@ -1,543 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: WXList.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a non-MFC generic template list
|
||||
// class.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/* A generic list of pointers to objects.
|
||||
No storage management or copying is done on the objects pointed to.
|
||||
Objectives: avoid using MFC libraries in ndm kernel mode and
|
||||
provide a really useful list type.
|
||||
|
||||
The class is thread safe in that separate threads may add and
|
||||
delete items in the list concurrently although the application
|
||||
must ensure that constructor and destructor access is suitably
|
||||
synchronised. An application can cause deadlock with operations
|
||||
which use two lists by simultaneously calling
|
||||
list1->Operation(list2) and list2->Operation(list1). So don't!
|
||||
|
||||
The names must not conflict with MFC classes as an application
|
||||
may use both.
|
||||
*/
|
||||
|
||||
#ifndef __WXLIST__
|
||||
#define __WXLIST__
|
||||
|
||||
/* A POSITION represents (in some fashion that's opaque) a cursor
|
||||
on the list that can be set to identify any element. NULL is
|
||||
a valid value and several operations regard NULL as the position
|
||||
"one step off the end of the list". (In an n element list there
|
||||
are n+1 places to insert and NULL is that "n+1-th" value).
|
||||
The POSITION of an element in the list is only invalidated if
|
||||
that element is deleted. Move operations may mean that what
|
||||
was a valid POSITION in one list is now a valid POSITION in
|
||||
a different list.
|
||||
|
||||
Some operations which at first sight are illegal are allowed as
|
||||
harmless no-ops. For instance RemoveHead is legal on an empty
|
||||
list and it returns NULL. This allows an atomic way to test if
|
||||
there is an element there, and if so, get it. The two operations
|
||||
AddTail and RemoveHead thus implement a MONITOR (See Hoare's paper).
|
||||
|
||||
Single element operations return POSITIONs, non-NULL means it worked.
|
||||
whole list operations return a BOOL. TRUE means it all worked.
|
||||
|
||||
This definition is the same as the POSITION type for MFCs, so we must
|
||||
avoid defining it twice.
|
||||
*/
|
||||
struct __WXLIST_POSITION { int unused; };
|
||||
typedef __WXLIST_POSITION* WXLIST_POSITION;
|
||||
|
||||
const int DEFAULTCACHE = 10; /* Default node object cache size */
|
||||
|
||||
/* A class representing one node in a list.
|
||||
Each node knows a pointer to it's adjacent nodes and also a pointer
|
||||
to the object that it looks after.
|
||||
All of these pointers can be retrieved or set through member functions.
|
||||
*/
|
||||
class CBaseList
|
||||
#ifdef DEBUG
|
||||
: public CBaseObject
|
||||
#endif
|
||||
{
|
||||
/* Making these classes inherit from CBaseObject does nothing
|
||||
functionally but it allows us to check there are no memory
|
||||
leaks in debug builds.
|
||||
*/
|
||||
|
||||
public:
|
||||
|
||||
#ifdef DEBUG
|
||||
class CNode : public CBaseObject {
|
||||
#else
|
||||
class CNode {
|
||||
#endif
|
||||
|
||||
CNode *m_pPrev; /* Previous node in the list */
|
||||
CNode *m_pNext; /* Next node in the list */
|
||||
void *m_pObject; /* Pointer to the object */
|
||||
|
||||
public:
|
||||
|
||||
/* Constructor - initialise the object's pointers */
|
||||
CNode()
|
||||
#ifdef DEBUG
|
||||
: CBaseObject(NAME("List node"))
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/* Return the previous node before this one */
|
||||
CNode *Prev() const { return m_pPrev; };
|
||||
|
||||
|
||||
/* Return the next node after this one */
|
||||
CNode *Next() const { return m_pNext; };
|
||||
|
||||
|
||||
/* Set the previous node before this one */
|
||||
void SetPrev(CNode *p) { m_pPrev = p; };
|
||||
|
||||
|
||||
/* Set the next node after this one */
|
||||
void SetNext(CNode *p) { m_pNext = p; };
|
||||
|
||||
|
||||
/* Get the pointer to the object for this node */
|
||||
void *GetData() const { return m_pObject; };
|
||||
|
||||
|
||||
/* Set the pointer to the object for this node */
|
||||
void SetData(void *p) { m_pObject = p; };
|
||||
};
|
||||
|
||||
class CNodeCache
|
||||
{
|
||||
public:
|
||||
CNodeCache(INT iCacheSize) : m_iCacheSize(iCacheSize),
|
||||
m_pHead(NULL),
|
||||
m_iUsed(0)
|
||||
{};
|
||||
~CNodeCache() {
|
||||
CNode *pNode = m_pHead;
|
||||
while (pNode) {
|
||||
CNode *pCurrent = pNode;
|
||||
pNode = pNode->Next();
|
||||
delete pCurrent;
|
||||
}
|
||||
};
|
||||
void AddToCache(CNode *pNode)
|
||||
{
|
||||
if (m_iUsed < m_iCacheSize) {
|
||||
pNode->SetNext(m_pHead);
|
||||
m_pHead = pNode;
|
||||
m_iUsed++;
|
||||
} else {
|
||||
delete pNode;
|
||||
}
|
||||
};
|
||||
CNode *RemoveFromCache()
|
||||
{
|
||||
CNode *pNode = m_pHead;
|
||||
if (pNode != NULL) {
|
||||
m_pHead = pNode->Next();
|
||||
m_iUsed--;
|
||||
ASSERT(m_iUsed >= 0);
|
||||
} else {
|
||||
ASSERT(m_iUsed == 0);
|
||||
}
|
||||
return pNode;
|
||||
};
|
||||
private:
|
||||
INT m_iCacheSize;
|
||||
INT m_iUsed;
|
||||
CNode *m_pHead;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
CNode* m_pFirst; /* Pointer to first node in the list */
|
||||
CNode* m_pLast; /* Pointer to the last node in the list */
|
||||
LONG m_Count; /* Number of nodes currently in the list */
|
||||
|
||||
private:
|
||||
|
||||
CNodeCache m_Cache; /* Cache of unused node pointers */
|
||||
|
||||
private:
|
||||
|
||||
/* These override the default copy constructor and assignment
|
||||
operator for all list classes. They are in the private class
|
||||
declaration section so that anybody trying to pass a list
|
||||
object by value will generate a compile time error of
|
||||
"cannot access the private member function". If these were
|
||||
not here then the compiler will create default constructors
|
||||
and assignment operators which when executed first take a
|
||||
copy of all member variables and then during destruction
|
||||
delete them all. This must not be done for any heap
|
||||
allocated data.
|
||||
*/
|
||||
CBaseList(const CBaseList &refList);
|
||||
CBaseList &operator=(const CBaseList &refList);
|
||||
|
||||
public:
|
||||
|
||||
CBaseList(TCHAR *pName,
|
||||
INT iItems);
|
||||
|
||||
CBaseList(TCHAR *pName);
|
||||
#ifdef UNICODE
|
||||
CBaseList(CHAR *pName,
|
||||
INT iItems);
|
||||
|
||||
CBaseList(CHAR *pName);
|
||||
#endif
|
||||
~CBaseList();
|
||||
|
||||
/* Remove all the nodes from *this i.e. make the list empty */
|
||||
void RemoveAll();
|
||||
|
||||
|
||||
/* Return a cursor which identifies the first element of *this */
|
||||
WXLIST_POSITION GetHeadPositionI() const;
|
||||
|
||||
|
||||
/* Return a cursor which identifies the last element of *this */
|
||||
WXLIST_POSITION GetTailPositionI() const;
|
||||
|
||||
|
||||
/* Return the number of objects in *this */
|
||||
int GetCountI() const;
|
||||
|
||||
protected:
|
||||
/* Return the pointer to the object at rp,
|
||||
Update rp to the next node in *this
|
||||
but make it NULL if it was at the end of *this.
|
||||
This is a wart retained for backwards compatibility.
|
||||
GetPrev is not implemented.
|
||||
Use Next, Prev and Get separately.
|
||||
*/
|
||||
void *GetNextI(WXLIST_POSITION& rp) const;
|
||||
|
||||
|
||||
/* Return a pointer to the object at p
|
||||
Asking for the object at NULL will return NULL harmlessly.
|
||||
*/
|
||||
void *GetI(WXLIST_POSITION p) const;
|
||||
|
||||
public:
|
||||
/* return the next / prev position in *this
|
||||
return NULL when going past the end/start.
|
||||
Next(NULL) is same as GetHeadPosition()
|
||||
Prev(NULL) is same as GetTailPosition()
|
||||
An n element list therefore behaves like a n+1 element
|
||||
cycle with NULL at the start/end.
|
||||
|
||||
!!WARNING!! - This handling of NULL is DIFFERENT from GetNext.
|
||||
|
||||
Some reasons are:
|
||||
1. For a list of n items there are n+1 positions to insert
|
||||
These are conveniently encoded as the n POSITIONs and NULL.
|
||||
2. If you are keeping a list sorted (fairly common) and you
|
||||
search forward for an element to insert before and don't
|
||||
find it you finish up with NULL as the element before which
|
||||
to insert. You then want that NULL to be a valid WXLIST_POSITION
|
||||
so that you can insert before it and you want that insertion
|
||||
point to mean the (n+1)-th one that doesn't have a WXLIST_POSITION.
|
||||
(symmetrically if you are working backwards through the list).
|
||||
3. It simplifies the algebra which the methods generate.
|
||||
e.g. AddBefore(p,x) is identical to AddAfter(Prev(p),x)
|
||||
in ALL cases. All the other arguments probably are reflections
|
||||
of the algebraic point.
|
||||
*/
|
||||
WXLIST_POSITION Next(WXLIST_POSITION pos) const
|
||||
{
|
||||
if (pos == NULL) {
|
||||
return (WXLIST_POSITION) m_pFirst;
|
||||
}
|
||||
CNode *pn = (CNode *) pos;
|
||||
return (WXLIST_POSITION) pn->Next();
|
||||
} //Next
|
||||
|
||||
// See Next
|
||||
WXLIST_POSITION Prev(WXLIST_POSITION pos) const
|
||||
{
|
||||
if (pos == NULL) {
|
||||
return (WXLIST_POSITION) m_pLast;
|
||||
}
|
||||
CNode *pn = (CNode *) pos;
|
||||
return (WXLIST_POSITION) pn->Prev();
|
||||
} //Prev
|
||||
|
||||
|
||||
/* Return the first position in *this which holds the given
|
||||
pointer. Return NULL if the pointer was not not found.
|
||||
*/
|
||||
protected:
|
||||
WXLIST_POSITION FindI( void * pObj) const;
|
||||
|
||||
/* Remove the first node in *this (deletes the pointer to its
|
||||
object from the list, does not free the object itself).
|
||||
Return the pointer to its object.
|
||||
If *this was already empty it will harmlessly return NULL.
|
||||
*/
|
||||
void *RemoveHeadI();
|
||||
|
||||
|
||||
/* Remove the last node in *this (deletes the pointer to its
|
||||
object from the list, does not free the object itself).
|
||||
Return the pointer to its object.
|
||||
If *this was already empty it will harmlessly return NULL.
|
||||
*/
|
||||
void *RemoveTailI();
|
||||
|
||||
|
||||
/* Remove the node identified by p from the list (deletes the pointer
|
||||
to its object from the list, does not free the object itself).
|
||||
Asking to Remove the object at NULL will harmlessly return NULL.
|
||||
Return the pointer to the object removed.
|
||||
*/
|
||||
void *RemoveI(WXLIST_POSITION p);
|
||||
|
||||
/* Add single object *pObj to become a new last element of the list.
|
||||
Return the new tail position, NULL if it fails.
|
||||
If you are adding a COM objects, you might want AddRef it first.
|
||||
Other existing POSITIONs in *this are still valid
|
||||
*/
|
||||
WXLIST_POSITION AddTailI(void * pObj);
|
||||
public:
|
||||
|
||||
|
||||
/* Add all the elements in *pList to the tail of *this.
|
||||
This duplicates all the nodes in *pList (i.e. duplicates
|
||||
all its pointers to objects). It does not duplicate the objects.
|
||||
If you are adding a list of pointers to a COM object into the list
|
||||
it's a good idea to AddRef them all it when you AddTail it.
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails some elements may have been added.
|
||||
Existing POSITIONs in *this are still valid
|
||||
|
||||
If you actually want to MOVE the elements, use MoveToTail instead.
|
||||
*/
|
||||
BOOL AddTail(CBaseList *pList);
|
||||
|
||||
|
||||
/* Mirror images of AddHead: */
|
||||
|
||||
/* Add single object to become a new first element of the list.
|
||||
Return the new head position, NULL if it fails.
|
||||
Existing POSITIONs in *this are still valid
|
||||
*/
|
||||
protected:
|
||||
WXLIST_POSITION AddHeadI(void * pObj);
|
||||
public:
|
||||
|
||||
/* Add all the elements in *pList to the head of *this.
|
||||
Same warnings apply as for AddTail.
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails some of the objects may have been added.
|
||||
|
||||
If you actually want to MOVE the elements, use MoveToHead instead.
|
||||
*/
|
||||
BOOL AddHead(CBaseList *pList);
|
||||
|
||||
|
||||
/* Add the object *pObj to *this after position p in *this.
|
||||
AddAfter(NULL,x) adds x to the start - equivalent to AddHead
|
||||
Return the position of the object added, NULL if it failed.
|
||||
Existing POSITIONs in *this are undisturbed, including p.
|
||||
*/
|
||||
protected:
|
||||
WXLIST_POSITION AddAfterI(WXLIST_POSITION p, void * pObj);
|
||||
public:
|
||||
|
||||
/* Add the list *pList to *this after position p in *this
|
||||
AddAfter(NULL,x) adds x to the start - equivalent to AddHead
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails, some of the objects may be added
|
||||
Existing POSITIONs in *this are undisturbed, including p.
|
||||
*/
|
||||
BOOL AddAfter(WXLIST_POSITION p, CBaseList *pList);
|
||||
|
||||
|
||||
/* Mirror images:
|
||||
Add the object *pObj to this-List after position p in *this.
|
||||
AddBefore(NULL,x) adds x to the end - equivalent to AddTail
|
||||
Return the position of the new object, NULL if it fails
|
||||
Existing POSITIONs in *this are undisturbed, including p.
|
||||
*/
|
||||
protected:
|
||||
WXLIST_POSITION AddBeforeI(WXLIST_POSITION p, void * pObj);
|
||||
public:
|
||||
|
||||
/* Add the list *pList to *this before position p in *this
|
||||
AddAfter(NULL,x) adds x to the start - equivalent to AddHead
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails, some of the objects may be added
|
||||
Existing POSITIONs in *this are undisturbed, including p.
|
||||
*/
|
||||
BOOL AddBefore(WXLIST_POSITION p, CBaseList *pList);
|
||||
|
||||
|
||||
/* Note that AddAfter(p,x) is equivalent to AddBefore(Next(p),x)
|
||||
even in cases where p is NULL or Next(p) is NULL.
|
||||
Similarly for mirror images etc.
|
||||
This may make it easier to argue about programs.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* The following operations do not copy any elements.
|
||||
They move existing blocks of elements around by switching pointers.
|
||||
They are fairly efficient for long lists as for short lists.
|
||||
(Alas, the Count slows things down).
|
||||
|
||||
They split the list into two parts.
|
||||
One part remains as the original list, the other part
|
||||
is appended to the second list. There are eight possible
|
||||
variations:
|
||||
Split the list {after/before} a given element
|
||||
keep the {head/tail} portion in the original list
|
||||
append the rest to the {head/tail} of the new list.
|
||||
|
||||
Since After is strictly equivalent to Before Next
|
||||
we are not in serious need of the Before/After variants.
|
||||
That leaves only four.
|
||||
|
||||
If you are processing a list left to right and dumping
|
||||
the bits that you have processed into another list as
|
||||
you go, the Tail/Tail variant gives the most natural result.
|
||||
If you are processing in reverse order, Head/Head is best.
|
||||
|
||||
By using NULL positions and empty lists judiciously either
|
||||
of the other two can be built up in two operations.
|
||||
|
||||
The definition of NULL (see Next/Prev etc) means that
|
||||
degenerate cases include
|
||||
"move all elements to new list"
|
||||
"Split a list into two lists"
|
||||
"Concatenate two lists"
|
||||
(and quite a few no-ops)
|
||||
|
||||
!!WARNING!! The type checking won't buy you much if you get list
|
||||
positions muddled up - e.g. use a WXLIST_POSITION that's in a different
|
||||
list and see what a mess you get!
|
||||
*/
|
||||
|
||||
/* Split *this after position p in *this
|
||||
Retain as *this the tail portion of the original *this
|
||||
Add the head portion to the tail end of *pList
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
|
||||
e.g.
|
||||
foo->MoveToTail(foo->GetHeadPosition(), bar);
|
||||
moves one element from the head of foo to the tail of bar
|
||||
foo->MoveToTail(NULL, bar);
|
||||
is a no-op, returns NULL
|
||||
foo->MoveToTail(foo->GetTailPosition, bar);
|
||||
concatenates foo onto the end of bar and empties foo.
|
||||
|
||||
A better, except excessively long name might be
|
||||
MoveElementsFromHeadThroughPositionToOtherTail
|
||||
*/
|
||||
BOOL MoveToTail(WXLIST_POSITION pos, CBaseList *pList);
|
||||
|
||||
|
||||
/* Mirror image:
|
||||
Split *this before position p in *this.
|
||||
Retain in *this the head portion of the original *this
|
||||
Add the tail portion to the start (i.e. head) of *pList
|
||||
|
||||
e.g.
|
||||
foo->MoveToHead(foo->GetTailPosition(), bar);
|
||||
moves one element from the tail of foo to the head of bar
|
||||
foo->MoveToHead(NULL, bar);
|
||||
is a no-op, returns NULL
|
||||
foo->MoveToHead(foo->GetHeadPosition, bar);
|
||||
concatenates foo onto the start of bar and empties foo.
|
||||
*/
|
||||
BOOL MoveToHead(WXLIST_POSITION pos, CBaseList *pList);
|
||||
|
||||
|
||||
/* Reverse the order of the [pointers to] objects in *this
|
||||
*/
|
||||
void Reverse();
|
||||
|
||||
|
||||
/* set cursor to the position of each element of list in turn */
|
||||
#define TRAVERSELIST(list, cursor) \
|
||||
for ( cursor = (list).GetHeadPosition() \
|
||||
; cursor!=NULL \
|
||||
; cursor = (list).Next(cursor) \
|
||||
)
|
||||
|
||||
|
||||
/* set cursor to the position of each element of list in turn
|
||||
in reverse order
|
||||
*/
|
||||
#define REVERSETRAVERSELIST(list, cursor) \
|
||||
for ( cursor = (list).GetTailPosition() \
|
||||
; cursor!=NULL \
|
||||
; cursor = (list).Prev(cursor) \
|
||||
)
|
||||
|
||||
}; // end of class declaration
|
||||
|
||||
template<class OBJECT> class CGenericList : public CBaseList
|
||||
{
|
||||
public:
|
||||
CGenericList(TCHAR *pName,
|
||||
INT iItems,
|
||||
BOOL bLock = TRUE,
|
||||
BOOL bAlert = FALSE) :
|
||||
CBaseList(pName, iItems) {
|
||||
UNREFERENCED_PARAMETER(bAlert);
|
||||
UNREFERENCED_PARAMETER(bLock);
|
||||
};
|
||||
CGenericList(TCHAR *pName) :
|
||||
CBaseList(pName) {
|
||||
};
|
||||
|
||||
WXLIST_POSITION GetHeadPosition() const { return (WXLIST_POSITION)m_pFirst; }
|
||||
WXLIST_POSITION GetTailPosition() const { return (WXLIST_POSITION)m_pLast; }
|
||||
int GetCount() const { return m_Count; }
|
||||
|
||||
OBJECT *GetNext(WXLIST_POSITION& rp) const { return (OBJECT *) GetNextI(rp); }
|
||||
|
||||
OBJECT *Get(WXLIST_POSITION p) const { return (OBJECT *) GetI(p); }
|
||||
OBJECT *GetHead() const { return Get(GetHeadPosition()); }
|
||||
|
||||
OBJECT *RemoveHead() { return (OBJECT *) RemoveHeadI(); }
|
||||
|
||||
OBJECT *RemoveTail() { return (OBJECT *) RemoveTailI(); }
|
||||
|
||||
OBJECT *Remove(WXLIST_POSITION p) { return (OBJECT *) RemoveI(p); }
|
||||
WXLIST_POSITION AddBefore(WXLIST_POSITION p, OBJECT * pObj) { return AddBeforeI(p, pObj); }
|
||||
WXLIST_POSITION AddAfter(WXLIST_POSITION p, OBJECT * pObj) { return AddAfterI(p, pObj); }
|
||||
WXLIST_POSITION AddHead(OBJECT * pObj) { return AddHeadI(pObj); }
|
||||
WXLIST_POSITION AddTail(OBJECT * pObj) { return AddTailI(pObj); }
|
||||
BOOL AddTail(CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddTail((CBaseList *) pList); }
|
||||
BOOL AddHead(CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddHead((CBaseList *) pList); }
|
||||
BOOL AddAfter(WXLIST_POSITION p, CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddAfter(p, (CBaseList *) pList); };
|
||||
BOOL AddBefore(WXLIST_POSITION p, CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddBefore(p, (CBaseList *) pList); };
|
||||
WXLIST_POSITION Find( OBJECT * pObj) const { return FindI(pObj); }
|
||||
}; // end of class declaration
|
||||
|
||||
|
||||
|
||||
/* These define the standard list types */
|
||||
|
||||
typedef CGenericList<CBaseObject> CBaseObjectList;
|
||||
typedef CGenericList<IUnknown> CBaseInterfaceList;
|
||||
|
||||
#endif /* __WXLIST__ */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,541 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: WXUtil.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines helper classes and functions for
|
||||
// building multimedia filters.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __WXUTIL__
|
||||
#define __WXUTIL__
|
||||
|
||||
// eliminate spurious "statement has no effect" warnings.
|
||||
#pragma warning(disable: 4705)
|
||||
|
||||
// wrapper for whatever critical section we have
|
||||
class CCritSec {
|
||||
|
||||
// make copy constructor and assignment operator inaccessible
|
||||
|
||||
CCritSec(const CCritSec &refCritSec);
|
||||
CCritSec &operator=(const CCritSec &refCritSec);
|
||||
|
||||
CRITICAL_SECTION m_CritSec;
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
DWORD m_id;
|
||||
DWORD m_currentOwner;
|
||||
DWORD m_lockCount;
|
||||
BOOL m_fTrace; // Trace this one
|
||||
public:
|
||||
CCritSec(DWORD id = 0);
|
||||
~CCritSec();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
#else
|
||||
|
||||
public:
|
||||
CCritSec() {
|
||||
InitializeCriticalSection(&m_CritSec);
|
||||
};
|
||||
|
||||
~CCritSec() {
|
||||
DeleteCriticalSection(&m_CritSec);
|
||||
};
|
||||
|
||||
void Lock() {
|
||||
EnterCriticalSection(&m_CritSec);
|
||||
};
|
||||
|
||||
void Unlock() {
|
||||
LeaveCriticalSection(&m_CritSec);
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
//
|
||||
// To make deadlocks easier to track it is useful to insert in the
|
||||
// code an assertion that says whether we own a critical section or
|
||||
// not. We make the routines that do the checking globals to avoid
|
||||
// having different numbers of member functions in the debug and
|
||||
// retail class implementations of CCritSec. In addition we provide
|
||||
// a routine that allows usage of specific critical sections to be
|
||||
// traced. This is NOT on by default - there are far too many.
|
||||
//
|
||||
|
||||
#ifdef DEBUG
|
||||
BOOL WINAPI CritCheckIn(CCritSec * pcCrit);
|
||||
BOOL WINAPI CritCheckIn(const CCritSec * pcCrit);
|
||||
BOOL WINAPI CritCheckOut(CCritSec * pcCrit);
|
||||
BOOL WINAPI CritCheckOut(const CCritSec * pcCrit);
|
||||
void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace);
|
||||
#else
|
||||
#define CritCheckIn(x) TRUE
|
||||
#define CritCheckOut(x) TRUE
|
||||
#define DbgLockTrace(pc, fT)
|
||||
#endif
|
||||
|
||||
|
||||
// locks a critical section, and unlocks it automatically
|
||||
// when the lock goes out of scope
|
||||
class CAutoLock {
|
||||
|
||||
// make copy constructor and assignment operator inaccessible
|
||||
|
||||
CAutoLock(const CAutoLock &refAutoLock);
|
||||
CAutoLock &operator=(const CAutoLock &refAutoLock);
|
||||
|
||||
protected:
|
||||
CCritSec * m_pLock;
|
||||
|
||||
public:
|
||||
CAutoLock(CCritSec * plock)
|
||||
{
|
||||
m_pLock = plock;
|
||||
m_pLock->Lock();
|
||||
};
|
||||
|
||||
~CAutoLock() {
|
||||
m_pLock->Unlock();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
// wrapper for event objects
|
||||
class CAMEvent
|
||||
{
|
||||
|
||||
// make copy constructor and assignment operator inaccessible
|
||||
|
||||
CAMEvent(const CAMEvent &refEvent);
|
||||
CAMEvent &operator=(const CAMEvent &refEvent);
|
||||
|
||||
protected:
|
||||
HANDLE m_hEvent;
|
||||
public:
|
||||
CAMEvent(BOOL fManualReset = FALSE);
|
||||
~CAMEvent();
|
||||
|
||||
// Cast to HANDLE - we don't support this as an lvalue
|
||||
operator HANDLE () const { return m_hEvent; };
|
||||
|
||||
void Set() {EXECUTE_ASSERT(SetEvent(m_hEvent));};
|
||||
BOOL Wait(DWORD dwTimeout = INFINITE) {
|
||||
return (WaitForSingleObject(m_hEvent, dwTimeout) == WAIT_OBJECT_0);
|
||||
};
|
||||
void Reset() { ResetEvent(m_hEvent); };
|
||||
BOOL Check() { return Wait(0); };
|
||||
};
|
||||
|
||||
|
||||
// wrapper for event objects that do message processing
|
||||
// This adds ONE method to the CAMEvent object to allow sent
|
||||
// messages to be processed while waiting
|
||||
|
||||
class CAMMsgEvent : public CAMEvent
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// Allow SEND messages to be processed while waiting
|
||||
BOOL WaitMsg(DWORD dwTimeout = INFINITE);
|
||||
};
|
||||
|
||||
// old name supported for the time being
|
||||
#define CTimeoutEvent CAMEvent
|
||||
|
||||
// support for a worker thread
|
||||
|
||||
// simple thread class supports creation of worker thread, synchronization
|
||||
// and communication. Can be derived to simplify parameter passing
|
||||
class AM_NOVTABLE CAMThread {
|
||||
|
||||
// make copy constructor and assignment operator inaccessible
|
||||
|
||||
CAMThread(const CAMThread &refThread);
|
||||
CAMThread &operator=(const CAMThread &refThread);
|
||||
|
||||
CAMEvent m_EventSend;
|
||||
CAMEvent m_EventComplete;
|
||||
|
||||
DWORD m_dwParam;
|
||||
DWORD m_dwReturnVal;
|
||||
|
||||
protected:
|
||||
HANDLE m_hThread;
|
||||
|
||||
// thread will run this function on startup
|
||||
// must be supplied by derived class
|
||||
virtual DWORD ThreadProc() = 0;
|
||||
|
||||
public:
|
||||
CAMThread();
|
||||
virtual ~CAMThread();
|
||||
|
||||
CCritSec m_AccessLock; // locks access by client threads
|
||||
CCritSec m_WorkerLock; // locks access to shared objects
|
||||
|
||||
// thread initially runs this. param is actually 'this'. function
|
||||
// just gets this and calls ThreadProc
|
||||
static DWORD WINAPI InitialThreadProc(LPVOID pv);
|
||||
|
||||
// start thread running - error if already running
|
||||
BOOL Create();
|
||||
|
||||
// signal the thread, and block for a response
|
||||
//
|
||||
DWORD CallWorker(DWORD);
|
||||
|
||||
// accessor thread calls this when done with thread (having told thread
|
||||
// to exit)
|
||||
void Close() {
|
||||
#pragma warning( push )
|
||||
// C4312: 'type cast' : conversion from 'LONG' to 'PVOID' of greater size
|
||||
//
|
||||
// This code works correctly on 32-bit and 64-bit systems.
|
||||
#pragma warning( disable : 4312 )
|
||||
HANDLE hThread = (HANDLE)InterlockedExchangePointer(&m_hThread, 0);
|
||||
#pragma warning( pop )
|
||||
|
||||
if (hThread) {
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
CloseHandle(hThread);
|
||||
}
|
||||
};
|
||||
|
||||
// ThreadExists
|
||||
// Return TRUE if the thread exists. FALSE otherwise
|
||||
BOOL ThreadExists(void) const
|
||||
{
|
||||
if (m_hThread == 0) {
|
||||
return FALSE;
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// wait for the next request
|
||||
DWORD GetRequest();
|
||||
|
||||
// is there a request?
|
||||
BOOL CheckRequest(DWORD * pParam);
|
||||
|
||||
// reply to the request
|
||||
void Reply(DWORD);
|
||||
|
||||
// If you want to do WaitForMultipleObjects you'll need to include
|
||||
// this handle in your wait list or you won't be responsive
|
||||
HANDLE GetRequestHandle() const { return m_EventSend; };
|
||||
|
||||
// Find out what the request was
|
||||
DWORD GetRequestParam() const { return m_dwParam; };
|
||||
|
||||
// call CoInitializeEx (COINIT_DISABLE_OLE1DDE) if
|
||||
// available. S_FALSE means it's not available.
|
||||
static HRESULT CoInitializeHelper();
|
||||
};
|
||||
|
||||
|
||||
// CQueue
|
||||
//
|
||||
// Implements a simple Queue ADT. The queue contains a finite number of
|
||||
// objects, access to which is controlled by a semaphore. The semaphore
|
||||
// is created with an initial count (N). Each time an object is added
|
||||
// a call to WaitForSingleObject is made on the semaphore's handle. When
|
||||
// this function returns a slot has been reserved in the queue for the new
|
||||
// object. If no slots are available the function blocks until one becomes
|
||||
// available. Each time an object is removed from the queue ReleaseSemaphore
|
||||
// is called on the semaphore's handle, thus freeing a slot in the queue.
|
||||
// If no objects are present in the queue the function blocks until an
|
||||
// object has been added.
|
||||
|
||||
#define DEFAULT_QUEUESIZE 2
|
||||
|
||||
template <class T> class CQueue {
|
||||
private:
|
||||
HANDLE hSemPut; // Semaphore controlling queue "putting"
|
||||
HANDLE hSemGet; // Semaphore controlling queue "getting"
|
||||
CRITICAL_SECTION CritSect; // Thread seriallization
|
||||
int nMax; // Max objects allowed in queue
|
||||
int iNextPut; // Array index of next "PutMsg"
|
||||
int iNextGet; // Array index of next "GetMsg"
|
||||
T *QueueObjects; // Array of objects (ptr's to void)
|
||||
|
||||
void Initialize(int n) {
|
||||
iNextPut = iNextGet = 0;
|
||||
nMax = n;
|
||||
InitializeCriticalSection(&CritSect);
|
||||
hSemPut = CreateSemaphore(NULL, n, n, NULL);
|
||||
hSemGet = CreateSemaphore(NULL, 0, n, NULL);
|
||||
QueueObjects = new T[n];
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
CQueue(int n) {
|
||||
Initialize(n);
|
||||
}
|
||||
|
||||
CQueue() {
|
||||
Initialize(DEFAULT_QUEUESIZE);
|
||||
}
|
||||
|
||||
~CQueue() {
|
||||
delete [] QueueObjects;
|
||||
DeleteCriticalSection(&CritSect);
|
||||
CloseHandle(hSemPut);
|
||||
CloseHandle(hSemGet);
|
||||
}
|
||||
|
||||
T GetQueueObject() {
|
||||
int iSlot;
|
||||
T Object;
|
||||
LONG lPrevious;
|
||||
|
||||
// Wait for someone to put something on our queue, returns straight
|
||||
// away is there is already an object on the queue.
|
||||
//
|
||||
WaitForSingleObject(hSemGet, INFINITE);
|
||||
|
||||
EnterCriticalSection(&CritSect);
|
||||
iSlot = iNextGet++ % nMax;
|
||||
Object = QueueObjects[iSlot];
|
||||
LeaveCriticalSection(&CritSect);
|
||||
|
||||
// Release anyone waiting to put an object onto our queue as there
|
||||
// is now space available in the queue.
|
||||
//
|
||||
ReleaseSemaphore(hSemPut, 1L, &lPrevious);
|
||||
return Object;
|
||||
}
|
||||
|
||||
void PutQueueObject(T Object) {
|
||||
int iSlot;
|
||||
LONG lPrevious;
|
||||
|
||||
// Wait for someone to get something from our queue, returns straight
|
||||
// away is there is already an empty slot on the queue.
|
||||
//
|
||||
WaitForSingleObject(hSemPut, INFINITE);
|
||||
|
||||
EnterCriticalSection(&CritSect);
|
||||
iSlot = iNextPut++ % nMax;
|
||||
QueueObjects[iSlot] = Object;
|
||||
LeaveCriticalSection(&CritSect);
|
||||
|
||||
// Release anyone waiting to remove an object from our queue as there
|
||||
// is now an object available to be removed.
|
||||
//
|
||||
ReleaseSemaphore(hSemGet, 1L, &lPrevious);
|
||||
}
|
||||
};
|
||||
|
||||
// miscellaneous string conversion functions
|
||||
// NOTE: as we need to use the same binaries on Win95 as on NT this code should
|
||||
// be compiled WITHOUT unicode being defined. Otherwise we will not pick up
|
||||
// these internal routines and the binary will not run on Win95.
|
||||
|
||||
// int WINAPIV wsprintfWInternal(LPWSTR, LPCWSTR, ...);
|
||||
|
||||
//LPWSTR
|
||||
//WINAPI
|
||||
//lstrcpyWInternal(
|
||||
// LPWSTR lpString1,
|
||||
// LPCWSTR lpString2
|
||||
// );
|
||||
LPWSTR
|
||||
WINAPI
|
||||
lstrcpynWInternal(
|
||||
LPWSTR lpString1,
|
||||
LPCWSTR lpString2,
|
||||
int iMaxLength
|
||||
);
|
||||
int
|
||||
WINAPI
|
||||
lstrcmpWInternal(
|
||||
LPCWSTR lpString1,
|
||||
LPCWSTR lpString2
|
||||
);
|
||||
int
|
||||
WINAPI
|
||||
lstrcmpiWInternal(
|
||||
LPCWSTR lpString1,
|
||||
LPCWSTR lpString2
|
||||
);
|
||||
int
|
||||
WINAPI
|
||||
lstrlenWInternal(
|
||||
LPCWSTR lpString
|
||||
);
|
||||
|
||||
#ifndef UNICODE
|
||||
#define wsprintfW wsprintfWInternal
|
||||
#define lstrcpyW lstrcpyWInternal
|
||||
#define lstrcpynW lstrcpynWInternal
|
||||
#define lstrcmpW lstrcmpWInternal
|
||||
#define lstrcmpiW lstrcmpiWInternal
|
||||
#define lstrlenW lstrlenWInternal
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
void * __stdcall memmoveInternal(void *, const void *, size_t);
|
||||
|
||||
inline void * __cdecl memchrInternal(const void *buf, int chr, size_t cnt)
|
||||
{
|
||||
#ifdef _X86_
|
||||
void *pRet = NULL;
|
||||
|
||||
_asm {
|
||||
cld // make sure we get the direction right
|
||||
mov ecx, cnt // num of bytes to scan
|
||||
mov edi, buf // pointer byte stream
|
||||
mov eax, chr // byte to scan for
|
||||
repne scasb // look for the byte in the byte stream
|
||||
jnz exit_memchr // Z flag set if byte found
|
||||
dec edi // scasb always increments edi even when it
|
||||
// finds the required byte
|
||||
mov pRet, edi
|
||||
exit_memchr:
|
||||
}
|
||||
return pRet;
|
||||
|
||||
#else
|
||||
while ( cnt && (*(unsigned char *)buf != (unsigned char)chr) ) {
|
||||
buf = (unsigned char *)buf + 1;
|
||||
cnt--;
|
||||
}
|
||||
|
||||
return(cnt ? (void *)buf : NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WINAPI IntToWstr(int i, LPWSTR wstr, size_t len);
|
||||
|
||||
#define WstrToInt(sz) _wtoi(sz)
|
||||
#define atoiW(sz) _wtoi(sz)
|
||||
#define atoiA(sz) atoi(sz)
|
||||
|
||||
// These are available to help managing bitmap VIDEOINFOHEADER media structures
|
||||
|
||||
extern const DWORD bits555[3];
|
||||
extern const DWORD bits565[3];
|
||||
extern const DWORD bits888[3];
|
||||
|
||||
// These help convert between VIDEOINFOHEADER and BITMAPINFO structures
|
||||
|
||||
STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader);
|
||||
STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader);
|
||||
STDAPI_(WORD) GetBitCount(const GUID *pSubtype);
|
||||
|
||||
// strmbase.lib implements this for compatibility with people who
|
||||
// managed to link to this directly. we don't want to advertise it.
|
||||
//
|
||||
// STDAPI_(/* T */ CHAR *) GetSubtypeName(const GUID *pSubtype);
|
||||
|
||||
STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype);
|
||||
STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype);
|
||||
|
||||
#ifdef UNICODE
|
||||
#define GetSubtypeName GetSubtypeNameW
|
||||
#else
|
||||
#define GetSubtypeName GetSubtypeNameA
|
||||
#endif
|
||||
|
||||
STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader);
|
||||
STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader);
|
||||
STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo);
|
||||
STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo);
|
||||
|
||||
|
||||
// Compares two interfaces and returns TRUE if they are on the same object
|
||||
BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond);
|
||||
|
||||
// This is for comparing pins
|
||||
#define EqualPins(pPin1, pPin2) IsEqualObject(pPin1, pPin2)
|
||||
|
||||
|
||||
// Arithmetic helper functions
|
||||
|
||||
// Compute (a * b + rnd) / c
|
||||
LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG rnd);
|
||||
LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG rnd);
|
||||
|
||||
|
||||
// Avoids us dyna-linking to SysAllocString to copy BSTR strings
|
||||
STDAPI WriteBSTR(BSTR * pstrDest, LPCWSTR szSrc);
|
||||
STDAPI FreeBSTR(BSTR* pstr);
|
||||
|
||||
// Return a wide string - allocating memory for it
|
||||
// Returns:
|
||||
// S_OK - no error
|
||||
// E_POINTER - ppszReturn == NULL
|
||||
// E_OUTOFMEMORY - can't allocate memory for returned string
|
||||
STDAPI AMGetWideString(LPCWSTR pszString, LPWSTR *ppszReturn);
|
||||
|
||||
// Special wait for objects owning windows
|
||||
DWORD WINAPI WaitDispatchingMessages(
|
||||
HANDLE hObject,
|
||||
DWORD dwWait,
|
||||
HWND hwnd = NULL,
|
||||
UINT uMsg = 0,
|
||||
HANDLE hEvent = NULL);
|
||||
|
||||
// HRESULT_FROM_WIN32 converts ERROR_SUCCESS to a success code, but in
|
||||
// our use of HRESULT_FROM_WIN32, it typically means a function failed
|
||||
// to call SetLastError(), and we still want a failure code.
|
||||
//
|
||||
#define AmHresultFromWin32(x) (MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, x))
|
||||
|
||||
// call GetLastError and return an HRESULT value that will fail the
|
||||
// SUCCEEDED() macro.
|
||||
HRESULT AmGetLastErrorToHResult(void);
|
||||
|
||||
// duplicate of ATL's CComPtr to avoid linker conflicts.
|
||||
|
||||
IUnknown* QzAtlComPtrAssign(IUnknown** pp, IUnknown* lp);
|
||||
|
||||
template <class T>
|
||||
class QzCComPtr
|
||||
{
|
||||
public:
|
||||
typedef T _PtrClass;
|
||||
QzCComPtr() {p=NULL;}
|
||||
QzCComPtr(T* lp)
|
||||
{
|
||||
if ((p = lp) != NULL)
|
||||
p->AddRef();
|
||||
}
|
||||
QzCComPtr(const QzCComPtr<T>& lp)
|
||||
{
|
||||
if ((p = lp.p) != NULL)
|
||||
p->AddRef();
|
||||
}
|
||||
~QzCComPtr() {if (p) p->Release();}
|
||||
void Release() {if (p) p->Release(); p=NULL;}
|
||||
operator T*() {return (T*)p;}
|
||||
T& operator*() {ASSERT(p!=NULL); return *p; }
|
||||
//The assert on operator& usually indicates a bug. If this is really
|
||||
//what is needed, however, take the address of the p member explicitly.
|
||||
T** operator&() { ASSERT(p==NULL); return &p; }
|
||||
T* operator->() { ASSERT(p!=NULL); return p; }
|
||||
T* operator=(T* lp){return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp);}
|
||||
T* operator=(const QzCComPtr<T>& lp)
|
||||
{
|
||||
return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp.p);
|
||||
}
|
||||
#if _MSC_VER>1020
|
||||
bool operator!(){return (p == NULL);}
|
||||
#else
|
||||
BOOL operator!(){return (p == NULL) ? TRUE : FALSE;}
|
||||
#endif
|
||||
T* p;
|
||||
};
|
||||
|
||||
MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent );
|
||||
bool TimeKillSynchronousFlagAvailable( void );
|
||||
|
||||
#endif /* __WXUTIL__ */
|
@ -13,7 +13,7 @@
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<DisableSpecificWarnings>4456;4458;4996;4995;4324;4100;4101;4201;4556;4127;4512;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>$(VTUNE_AMPLIFIER_XE_2015_DIR)include;$(SolutionDir)3rdparty;$(SolutionDir)3rdparty\libpng;$(SolutionDir)3rdparty\opencl;$(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(VTUNE_AMPLIFIER_XE_2015_DIR)include;$(SolutionDir)unfree;$(SolutionDir)3rdparty;$(SolutionDir)3rdparty\libpng;$(SolutionDir)3rdparty\opencl;$(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
Loading…
Reference in New Issue
Block a user