initial commit
This commit is contained in:
commit
636959b37b
2474 files changed
+1343178
No files matched your search
+5363
File diff suppressed because it is too large.
Load diff
+1587
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,280 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: AMVideo.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements helper functions for
|
||||
// bitmap formats.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <pjmedia-videodev/config.h>
|
||||
|
||||
#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Some necessary macros are missing from mingw version. They are here.
|
||||
See https://github.com/ofTheo/videoInput/blob/master/videoInputSrcAndDemos/libs/DShow/Include/amvideo.h
|
||||
*/
|
||||
//------------------------------------------------------------------------------
|
||||
// File: AMVideo.h
|
||||
//
|
||||
// Desc: Video related definitions and interfaces for ActiveMovie.
|
||||
//
|
||||
// Copyright (c) 1992 - 2001, Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define TRUECOLOR(pbmi) ((TRUECOLORINFO *)(((LPBYTE)&((pbmi)->bmiHeader)) \
|
||||
+ (pbmi)->bmiHeader.biSize))
|
||||
#define COLORS(pbmi) ((RGBQUAD *)(((LPBYTE)&((pbmi)->bmiHeader)) \
|
||||
+ (pbmi)->bmiHeader.biSize))
|
||||
|
||||
#define SIZE_MASKS (iMASK_COLORS * sizeof(DWORD))
|
||||
#define SIZE_PREHEADER (FIELD_OFFSET(VIDEOINFOHEADER,bmiHeader))
|
||||
|
||||
#define WIDTHBYTES(bits) ((DWORD)(((bits)+31) & (~31)) / 8)
|
||||
#define DIBWIDTHBYTES(bi) (DWORD)WIDTHBYTES((DWORD)(bi).biWidth * (DWORD)(bi).biBitCount)
|
||||
#define _DIBSIZE(bi) (DIBWIDTHBYTES(bi) * (DWORD)(bi).biHeight)
|
||||
#define DIBSIZE(bi) ((bi).biHeight < 0 ? (-1)*(_DIBSIZE(bi)) : _DIBSIZE(bi))
|
||||
|
||||
#define PALETTISED(pbmi) ((pbmi)->bmiHeader.biBitCount <= iPALETTE)
|
||||
@@ -0,0 +1,366 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: ArithUtil.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements helper classes for building
|
||||
// multimedia filters.
|
||||
//
|
||||
// Copyright (c) 1992-2004 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <pjmedia-videodev/config.h>
|
||||
|
||||
#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
|
||||
|
||||
#include <streams.h>
|
||||
|
||||
//
|
||||
// Declare function from largeint.h we need so that PPC can build
|
||||
//
|
||||
|
||||
//
|
||||
// Enlarged integer divide - 64-bits / 32-bits > 32-bits
|
||||
//
|
||||
|
||||
#if !defined(_X86_) || !defined(_MSC_VER)
|
||||
|
||||
#define LLtoU64(x) (*(unsigned __int64*)(void*)(&(x)))
|
||||
|
||||
__inline
|
||||
ULONG
|
||||
WINAPI
|
||||
EnlargedUnsignedDivide (
|
||||
IN ULARGE_INTEGER Dividend,
|
||||
IN ULONG Divisor,
|
||||
IN PULONG Remainder
|
||||
)
|
||||
{
|
||||
// return remainder if necessary
|
||||
if (Remainder != NULL)
|
||||
*Remainder = (ULONG)(LLtoU64(Dividend) % Divisor);
|
||||
return (ULONG)(LLtoU64(Dividend) / Divisor);
|
||||
}
|
||||
|
||||
#else
|
||||
__inline
|
||||
ULONG
|
||||
WINAPI
|
||||
EnlargedUnsignedDivide (
|
||||
IN ULARGE_INTEGER Dividend,
|
||||
IN ULONG Divisor,
|
||||
IN PULONG Remainder
|
||||
)
|
||||
{
|
||||
ULONG ulResult;
|
||||
_asm {
|
||||
mov eax,Dividend.LowPart
|
||||
mov edx,Dividend.HighPart
|
||||
mov ecx,Remainder
|
||||
div Divisor
|
||||
or ecx,ecx
|
||||
jz short label
|
||||
mov [ecx],edx
|
||||
label:
|
||||
mov ulResult,eax
|
||||
}
|
||||
return ulResult;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Arithmetic functions to help with time format conversions
|
||||
*/
|
||||
|
||||
#ifdef _M_ALPHA
|
||||
// work around bug in version 12.00.8385 of the alpha compiler where
|
||||
// UInt32x32To64 sign-extends its arguments (?)
|
||||
#undef UInt32x32To64
|
||||
#define UInt32x32To64(a, b) (((ULONGLONG)((ULONG)(a)) & 0xffffffff) * ((ULONGLONG)((ULONG)(b)) & 0xffffffff))
|
||||
#endif
|
||||
|
||||
/* Compute (a * b + d) / c */
|
||||
LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG d)
|
||||
{
|
||||
/* Compute the absolute values to avoid signed arithmetic problems */
|
||||
ULARGE_INTEGER ua, ub;
|
||||
DWORDLONG uc;
|
||||
|
||||
ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a);
|
||||
ub.QuadPart = (DWORDLONG)(b >= 0 ? b : -b);
|
||||
uc = (DWORDLONG)(c >= 0 ? c : -c);
|
||||
BOOL bSign = (a < 0) ^ (b < 0);
|
||||
|
||||
/* Do long multiplication */
|
||||
ULARGE_INTEGER p[2];
|
||||
p[0].QuadPart = UInt32x32To64(ua.LowPart, ub.LowPart);
|
||||
|
||||
/* This next computation cannot overflow into p[1].HighPart because
|
||||
the max number we can compute here is:
|
||||
|
||||
(2 ** 32 - 1) * (2 ** 32 - 1) + // ua.LowPart * ub.LowPart
|
||||
(2 ** 32) * (2 ** 31) * (2 ** 32 - 1) * 2 // x.LowPart * y.HighPart * 2
|
||||
|
||||
== 2 ** 96 - 2 ** 64 + (2 ** 64 - 2 ** 33 + 1)
|
||||
== 2 ** 96 - 2 ** 33 + 1
|
||||
< 2 ** 96
|
||||
*/
|
||||
|
||||
ULARGE_INTEGER x;
|
||||
x.QuadPart = UInt32x32To64(ua.LowPart, ub.HighPart) +
|
||||
UInt32x32To64(ua.HighPart, ub.LowPart) +
|
||||
p[0].HighPart;
|
||||
p[0].HighPart = x.LowPart;
|
||||
p[1].QuadPart = UInt32x32To64(ua.HighPart, ub.HighPart) + x.HighPart;
|
||||
|
||||
if (d != 0) {
|
||||
ULARGE_INTEGER ud[2];
|
||||
if (bSign) {
|
||||
ud[0].QuadPart = (DWORDLONG)(-d);
|
||||
if (d > 0) {
|
||||
/* -d < 0 */
|
||||
ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1;
|
||||
} else {
|
||||
ud[1].QuadPart = (DWORDLONG)0;
|
||||
}
|
||||
} else {
|
||||
ud[0].QuadPart = (DWORDLONG)d;
|
||||
if (d < 0) {
|
||||
ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1;
|
||||
} else {
|
||||
ud[1].QuadPart = (DWORDLONG)0;
|
||||
}
|
||||
}
|
||||
/* Now do extended addition */
|
||||
ULARGE_INTEGER uliTotal;
|
||||
|
||||
/* Add ls DWORDs */
|
||||
uliTotal.QuadPart = (DWORDLONG)ud[0].LowPart + p[0].LowPart;
|
||||
p[0].LowPart = uliTotal.LowPart;
|
||||
|
||||
/* Propagate carry */
|
||||
uliTotal.LowPart = uliTotal.HighPart;
|
||||
uliTotal.HighPart = 0;
|
||||
|
||||
/* Add 2nd most ls DWORDs */
|
||||
uliTotal.QuadPart += (DWORDLONG)ud[0].HighPart + p[0].HighPart;
|
||||
p[0].HighPart = uliTotal.LowPart;
|
||||
|
||||
/* Propagate carry */
|
||||
uliTotal.LowPart = uliTotal.HighPart;
|
||||
uliTotal.HighPart = 0;
|
||||
|
||||
/* Add MS DWORDLONGs - no carry expected */
|
||||
p[1].QuadPart += ud[1].QuadPart + uliTotal.QuadPart;
|
||||
|
||||
/* Now see if we got a sign change from the addition */
|
||||
if ((LONG)p[1].HighPart < 0) {
|
||||
bSign = !bSign;
|
||||
|
||||
/* Negate the current value (ugh!) */
|
||||
p[0].QuadPart = ~p[0].QuadPart;
|
||||
p[1].QuadPart = ~p[1].QuadPart;
|
||||
p[0].QuadPart += 1;
|
||||
p[1].QuadPart += (p[0].QuadPart == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now for the division */
|
||||
if (c < 0) {
|
||||
bSign = !bSign;
|
||||
}
|
||||
|
||||
|
||||
/* This will catch c == 0 and overflow */
|
||||
if (uc <= p[1].QuadPart) {
|
||||
return bSign ? (LONGLONG)0x8000000000000000 :
|
||||
(LONGLONG)0x7FFFFFFFFFFFFFFF;
|
||||
}
|
||||
|
||||
DWORDLONG ullResult;
|
||||
|
||||
/* Do the division */
|
||||
/* If the dividend is a DWORD_LONG use the compiler */
|
||||
if (p[1].QuadPart == 0) {
|
||||
ullResult = p[0].QuadPart / uc;
|
||||
return bSign ? -(LONGLONG)ullResult : (LONGLONG)ullResult;
|
||||
}
|
||||
|
||||
/* If the divisor is a DWORD then its simpler */
|
||||
ULARGE_INTEGER ulic;
|
||||
ulic.QuadPart = uc;
|
||||
if (ulic.HighPart == 0) {
|
||||
ULARGE_INTEGER uliDividend;
|
||||
ULARGE_INTEGER uliResult;
|
||||
DWORD dwDivisor = (DWORD)uc;
|
||||
// ASSERT(p[1].HighPart == 0 && p[1].LowPart < dwDivisor);
|
||||
uliDividend.HighPart = p[1].LowPart;
|
||||
uliDividend.LowPart = p[0].HighPart;
|
||||
#ifndef USE_LARGEINT
|
||||
uliResult.HighPart = (DWORD)(uliDividend.QuadPart / dwDivisor);
|
||||
p[0].HighPart = (DWORD)(uliDividend.QuadPart % dwDivisor);
|
||||
uliResult.LowPart = 0;
|
||||
uliResult.QuadPart = p[0].QuadPart / dwDivisor + uliResult.QuadPart;
|
||||
#else
|
||||
/* NOTE - this routine will take exceptions if
|
||||
the result does not fit in a DWORD
|
||||
*/
|
||||
if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) {
|
||||
uliResult.HighPart = EnlargedUnsignedDivide(
|
||||
uliDividend,
|
||||
dwDivisor,
|
||||
&p[0].HighPart);
|
||||
} else {
|
||||
uliResult.HighPart = 0;
|
||||
}
|
||||
uliResult.LowPart = EnlargedUnsignedDivide(
|
||||
p[0],
|
||||
dwDivisor,
|
||||
NULL);
|
||||
#endif
|
||||
return bSign ? -(LONGLONG)uliResult.QuadPart :
|
||||
(LONGLONG)uliResult.QuadPart;
|
||||
}
|
||||
|
||||
|
||||
ullResult = 0;
|
||||
|
||||
/* OK - do long division */
|
||||
for (int i = 0; i < 64; i++) {
|
||||
ullResult <<= 1;
|
||||
|
||||
/* Shift 128 bit p left 1 */
|
||||
p[1].QuadPart <<= 1;
|
||||
if ((p[0].HighPart & 0x80000000) != 0) {
|
||||
p[1].LowPart++;
|
||||
}
|
||||
p[0].QuadPart <<= 1;
|
||||
|
||||
/* Compare */
|
||||
if (uc <= p[1].QuadPart) {
|
||||
p[1].QuadPart -= uc;
|
||||
ullResult += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return bSign ? - (LONGLONG)ullResult : (LONGLONG)ullResult;
|
||||
}
|
||||
|
||||
LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG d)
|
||||
{
|
||||
ULARGE_INTEGER ua;
|
||||
DWORD ub;
|
||||
DWORD uc;
|
||||
|
||||
/* Compute the absolute values to avoid signed arithmetic problems */
|
||||
ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a);
|
||||
ub = (DWORD)(b >= 0 ? b : -b);
|
||||
uc = (DWORD)(c >= 0 ? c : -c);
|
||||
BOOL bSign = (a < 0) ^ (b < 0);
|
||||
|
||||
/* Do long multiplication */
|
||||
ULARGE_INTEGER p0;
|
||||
DWORD p1;
|
||||
p0.QuadPart = UInt32x32To64(ua.LowPart, ub);
|
||||
|
||||
if (ua.HighPart != 0) {
|
||||
ULARGE_INTEGER x;
|
||||
x.QuadPart = UInt32x32To64(ua.HighPart, ub) + p0.HighPart;
|
||||
p0.HighPart = x.LowPart;
|
||||
p1 = x.HighPart;
|
||||
} else {
|
||||
p1 = 0;
|
||||
}
|
||||
|
||||
if (d != 0) {
|
||||
ULARGE_INTEGER ud0;
|
||||
DWORD ud1;
|
||||
|
||||
if (bSign) {
|
||||
//
|
||||
// Cast d to LONGLONG first otherwise -0x80000000 sign extends
|
||||
// incorrectly
|
||||
//
|
||||
ud0.QuadPart = (DWORDLONG)(-(LONGLONG)d);
|
||||
if (d > 0) {
|
||||
/* -d < 0 */
|
||||
ud1 = (DWORD)-1;
|
||||
} else {
|
||||
ud1 = (DWORD)0;
|
||||
}
|
||||
} else {
|
||||
ud0.QuadPart = (DWORDLONG)d;
|
||||
if (d < 0) {
|
||||
ud1 = (DWORD)-1;
|
||||
} else {
|
||||
ud1 = (DWORD)0;
|
||||
}
|
||||
}
|
||||
/* Now do extended addition */
|
||||
ULARGE_INTEGER uliTotal;
|
||||
|
||||
/* Add ls DWORDs */
|
||||
uliTotal.QuadPart = (DWORDLONG)ud0.LowPart + p0.LowPart;
|
||||
p0.LowPart = uliTotal.LowPart;
|
||||
|
||||
/* Propagate carry */
|
||||
uliTotal.LowPart = uliTotal.HighPart;
|
||||
uliTotal.HighPart = 0;
|
||||
|
||||
/* Add 2nd most ls DWORDs */
|
||||
uliTotal.QuadPart += (DWORDLONG)ud0.HighPart + p0.HighPart;
|
||||
p0.HighPart = uliTotal.LowPart;
|
||||
|
||||
/* Add MS DWORDLONGs - no carry expected */
|
||||
p1 += ud1 + uliTotal.HighPart;
|
||||
|
||||
/* Now see if we got a sign change from the addition */
|
||||
if ((LONG)p1 < 0) {
|
||||
bSign = !bSign;
|
||||
|
||||
/* Negate the current value (ugh!) */
|
||||
p0.QuadPart = ~p0.QuadPart;
|
||||
p1 = ~p1;
|
||||
p0.QuadPart += 1;
|
||||
p1 += (p0.QuadPart == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now for the division */
|
||||
if (c < 0) {
|
||||
bSign = !bSign;
|
||||
}
|
||||
|
||||
|
||||
/* This will catch c == 0 and overflow */
|
||||
if (uc <= p1) {
|
||||
return bSign ? (LONGLONG)0x8000000000000000 :
|
||||
(LONGLONG)0x7FFFFFFFFFFFFFFF;
|
||||
}
|
||||
|
||||
/* Do the division */
|
||||
|
||||
/* If the divisor is a DWORD then its simpler */
|
||||
ULARGE_INTEGER uliDividend;
|
||||
ULARGE_INTEGER uliResult;
|
||||
DWORD dwDivisor = uc;
|
||||
uliDividend.HighPart = p1;
|
||||
uliDividend.LowPart = p0.HighPart;
|
||||
/* NOTE - this routine will take exceptions if
|
||||
the result does not fit in a DWORD
|
||||
*/
|
||||
if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) {
|
||||
uliResult.HighPart = EnlargedUnsignedDivide(
|
||||
uliDividend,
|
||||
dwDivisor,
|
||||
&p0.HighPart);
|
||||
} else {
|
||||
uliResult.HighPart = 0;
|
||||
}
|
||||
uliResult.LowPart = EnlargedUnsignedDivide(
|
||||
p0,
|
||||
dwDivisor,
|
||||
NULL);
|
||||
return bSign ? -(LONGLONG)uliResult.QuadPart :
|
||||
(LONGLONG)uliResult.QuadPart;
|
||||
}
|
||||
|
||||
#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */
|
||||
@@ -0,0 +1,269 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: ComBase.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements class hierarchy for creating
|
||||
// COM objects.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <pjmedia-videodev/config.h>
|
||||
|
||||
#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
|
||||
|
||||
#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(__in_opt LPCTSTR pName)
|
||||
{
|
||||
/* Increment the number of active objects */
|
||||
InterlockedIncrement(&m_cObjects);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#ifdef UNICODE
|
||||
m_dwCookie = DbgRegisterObjectCreation(0, pName);
|
||||
#else
|
||||
m_dwCookie = DbgRegisterObjectCreation(pName, 0);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
CBaseObject::CBaseObject(const char *pName)
|
||||
{
|
||||
/* Increment the number of active objects */
|
||||
InterlockedIncrement(&m_cObjects);
|
||||
|
||||
#ifdef DEBUG
|
||||
m_dwCookie = DbgRegisterObjectCreation(pName, 0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
HINSTANCE hlibOLEAut32;
|
||||
|
||||
/* Destructor */
|
||||
|
||||
CBaseObject::~CBaseObject()
|
||||
{
|
||||
/* Decrement the number of objects active */
|
||||
if (InterlockedDecrement(&m_cObjects) == 0) {
|
||||
if (hlibOLEAut32) {
|
||||
FreeLibrary(hlibOLEAut32);
|
||||
|
||||
hlibOLEAut32 = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
DbgRegisterObjectDestruction(m_dwCookie);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const TCHAR szOle32Aut[] = TEXT("OleAut32.dll");
|
||||
|
||||
HINSTANCE LoadOLEAut32()
|
||||
{
|
||||
if (hlibOLEAut32 == 0) {
|
||||
|
||||
hlibOLEAut32 = LoadLibrary(szOle32Aut);
|
||||
}
|
||||
|
||||
return hlibOLEAut32;
|
||||
}
|
||||
|
||||
|
||||
/* Constructor */
|
||||
|
||||
// We know we use "this" in the initialization list, we also know we don't modify *phr.
|
||||
#pragma warning( disable : 4355 4100 )
|
||||
CUnknown::CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk)
|
||||
: CBaseObject(pName)
|
||||
/* Start the object with a reference count of zero - when the */
|
||||
/* object is queried for it's first interface this may be */
|
||||
/* incremented depending on whether or not this object is */
|
||||
/* currently being aggregated upon */
|
||||
, m_cRef(0)
|
||||
/* Set our pointer to our IUnknown interface. */
|
||||
/* If we have an outer, use its, otherwise use ours. */
|
||||
/* This pointer effectivly points to the owner of */
|
||||
/* this object and can be accessed by the GetOwner() method. */
|
||||
, m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<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(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) :
|
||||
CBaseObject(pName),
|
||||
m_cRef(0),
|
||||
m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
CUnknown::CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk)
|
||||
: CBaseObject(pName), m_cRef(0),
|
||||
m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )
|
||||
{ }
|
||||
|
||||
CUnknown::CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) :
|
||||
CBaseObject(pName), m_cRef(0),
|
||||
m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )
|
||||
{ }
|
||||
|
||||
#endif
|
||||
|
||||
#pragma warning( default : 4355 4100 )
|
||||
|
||||
|
||||
/* QueryInterface */
|
||||
|
||||
STDMETHODIMP CUnknown::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)
|
||||
{
|
||||
CheckPointer(ppv,E_POINTER);
|
||||
ValidateReadWritePtr(ppv,sizeof(PVOID));
|
||||
|
||||
/* We know only about IUnknown */
|
||||
|
||||
if (riid == IID_IUnknown) {
|
||||
GetInterface((LPUNKNOWN) (PNDUNKNOWN) this, ppv);
|
||||
return NOERROR;
|
||||
} else {
|
||||
*ppv = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
/* We have to ensure that we DON'T use a max macro, since these will typically */
|
||||
/* lead to one of the parameters being evaluated twice. Since we are worried */
|
||||
/* about concurrency, we can't afford to access the m_cRef twice since we can't */
|
||||
/* afford to run the risk that its value having changed between accesses. */
|
||||
|
||||
template<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 {
|
||||
// Don't touch m_cRef again even in this leg as the object
|
||||
// may have just been released on another thread too
|
||||
return ourmax(ULONG(lRef), 1ul);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return an interface pointer to a requesting client
|
||||
performing a thread safe AddRef as necessary */
|
||||
|
||||
STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv)
|
||||
{
|
||||
CheckPointer(ppv, E_POINTER);
|
||||
*ppv = pUnk;
|
||||
pUnk->AddRef();
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
/* Compares two interfaces and returns TRUE if they are on the same object */
|
||||
|
||||
BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond)
|
||||
{
|
||||
/* Different objects can't have the same interface pointer for
|
||||
any interface
|
||||
*/
|
||||
if (pFirst == pSecond) {
|
||||
return TRUE;
|
||||
}
|
||||
/* OK - do it the hard way - check if they have the same
|
||||
IUnknown pointers - a single object can only have one of these
|
||||
*/
|
||||
LPUNKNOWN pUnknown1; // Retrieve the IUnknown interface
|
||||
LPUNKNOWN pUnknown2; // Retrieve the other IUnknown interface
|
||||
HRESULT hr; // General OLE return code
|
||||
|
||||
ASSERT(pFirst);
|
||||
ASSERT(pSecond);
|
||||
|
||||
/* See if the IUnknown pointers match */
|
||||
|
||||
hr = pFirst->QueryInterface(IID_IUnknown,(void **) &pUnknown1);
|
||||
if (FAILED(hr)) {
|
||||
return FALSE;
|
||||
}
|
||||
ASSERT(pUnknown1);
|
||||
|
||||
/* Release the extra interface we hold */
|
||||
|
||||
pUnknown1->Release();
|
||||
|
||||
hr = pSecond->QueryInterface(IID_IUnknown,(void **) &pUnknown2);
|
||||
if (FAILED(hr)) {
|
||||
return FALSE;
|
||||
}
|
||||
ASSERT(pUnknown2);
|
||||
|
||||
/* Release the extra interface we hold */
|
||||
|
||||
pUnknown2->Release();
|
||||
return (pUnknown1 == pUnknown2);
|
||||
}
|
||||
|
||||
#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: ComBase.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a class hierarchy for creating
|
||||
// COM objects.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*
|
||||
|
||||
a. Derive your COM object from CUnknown
|
||||
|
||||
b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT *
|
||||
and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls
|
||||
to. The HRESULT * allows error codes to be passed around constructors and
|
||||
the TCHAR * is a descriptive name that can be printed on the debugger.
|
||||
|
||||
It is important that constructors only change the HRESULT * if they have
|
||||
to set an ERROR code, if it was successful then leave it alone or you may
|
||||
overwrite an error code from an object previously created.
|
||||
|
||||
When you call a constructor the descriptive name should be in static store
|
||||
as we do not copy the string. To stop large amounts of memory being used
|
||||
in retail builds by all these static strings use the NAME macro,
|
||||
|
||||
CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class
|
||||
knows not to do anything with objects that don't have a name.
|
||||
|
||||
c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and
|
||||
TCHAR * to the CUnknown constructor. You can set the HRESULT if you have an
|
||||
error, or just simply pass it through to the constructor.
|
||||
|
||||
The object creation will fail in the class factory if the HRESULT indicates
|
||||
an error (ie FAILED(HRESULT) == TRUE)
|
||||
|
||||
d. Create a FactoryTemplate with your object's class id and CreateInstance
|
||||
function.
|
||||
|
||||
Then (for each interface) either
|
||||
|
||||
Multiple inheritance
|
||||
|
||||
1. Also derive it from ISomeInterface
|
||||
2. Include DECLARE_IUNKNOWN in your class definition to declare
|
||||
implementations of QueryInterface, AddRef and Release that
|
||||
call the outer unknown
|
||||
3. Override NonDelegatingQueryInterface to expose ISomeInterface by
|
||||
code something like
|
||||
|
||||
if (riid == IID_ISomeInterface) {
|
||||
return GetInterface((ISomeInterface *) this, ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
4. Declare and implement the member functions of ISomeInterface.
|
||||
|
||||
or: Nested interfaces
|
||||
|
||||
1. Declare a class derived from CUnknown
|
||||
2. Include DECLARE_IUNKNOWN in your class definition
|
||||
3. Override NonDelegatingQueryInterface to expose ISomeInterface by
|
||||
code something like
|
||||
|
||||
if (riid == IID_ISomeInterface) {
|
||||
return GetInterface((ISomeInterface *) this, ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
4. Implement the member functions of ISomeInterface. Use GetOwner() to
|
||||
access the COM object class.
|
||||
|
||||
And in your COM object class:
|
||||
|
||||
5. Make the nested class a friend of the COM object class, and declare
|
||||
an instance of the nested class as a member of the COM object class.
|
||||
|
||||
NOTE that because you must always pass the outer unknown and an hResult
|
||||
to the CUnknown constructor you cannot use a default constructor, in
|
||||
other words you will have to make the member variable a pointer to the
|
||||
class and make a NEW call in your constructor to actually create it.
|
||||
|
||||
6. override the NonDelegatingQueryInterface with code like this:
|
||||
|
||||
if (riid == IID_ISomeInterface) {
|
||||
return m_pImplFilter->
|
||||
NonDelegatingQueryInterface(IID_ISomeInterface, ppv);
|
||||
} else {
|
||||
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
You can have mixed classes which support some interfaces via multiple
|
||||
inheritance and some via nested classes
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __COMBASE__
|
||||
#define __COMBASE__
|
||||
|
||||
// Filter Setup data structures no defined in axextend.idl
|
||||
|
||||
typedef REGPINTYPES
|
||||
AMOVIESETUP_MEDIATYPE, * PAMOVIESETUP_MEDIATYPE, * FAR LPAMOVIESETUP_MEDIATYPE;
|
||||
|
||||
typedef REGFILTERPINS
|
||||
AMOVIESETUP_PIN, * PAMOVIESETUP_PIN, * FAR LPAMOVIESETUP_PIN;
|
||||
|
||||
typedef struct _AMOVIESETUP_FILTER
|
||||
{
|
||||
const CLSID * clsID;
|
||||
const WCHAR * strName;
|
||||
DWORD dwMerit;
|
||||
UINT nPins;
|
||||
const AMOVIESETUP_PIN * lpPin;
|
||||
}
|
||||
AMOVIESETUP_FILTER, * PAMOVIESETUP_FILTER, * FAR LPAMOVIESETUP_FILTER;
|
||||
|
||||
/* The DLLENTRY module initialises the module handle on loading */
|
||||
|
||||
extern HINSTANCE g_hInst;
|
||||
|
||||
/* On DLL load remember which platform we are running on */
|
||||
|
||||
extern DWORD g_amPlatform;
|
||||
extern OSVERSIONINFO g_osInfo; // Filled in by GetVersionEx
|
||||
|
||||
/* Version of IUnknown that is renamed to allow a class to support both
|
||||
non delegating and delegating IUnknowns in the same COM object */
|
||||
|
||||
#ifndef INONDELEGATINGUNKNOWN_DEFINED
|
||||
DECLARE_INTERFACE(INonDelegatingUnknown)
|
||||
{
|
||||
STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE;
|
||||
STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE;
|
||||
STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE;
|
||||
};
|
||||
#define INONDELEGATINGUNKNOWN_DEFINED
|
||||
#endif
|
||||
|
||||
typedef INonDelegatingUnknown *PNDUNKNOWN;
|
||||
|
||||
|
||||
/* This is the base object class that supports active object counting. As
|
||||
part of the debug facilities we trace every time a C++ object is created
|
||||
or destroyed. The name of the object has to be passed up through the class
|
||||
derivation list during construction as you cannot call virtual functions
|
||||
in the constructor. The downside of all this is that every single object
|
||||
constructor has to take an object name parameter that describes it */
|
||||
|
||||
class CBaseObject
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
// Disable the copy constructor and assignment by default so you will get
|
||||
// compiler errors instead of unexpected behaviour if you pass objects
|
||||
// by value or assign objects.
|
||||
CBaseObject(const CBaseObject& objectSrc); // no implementation
|
||||
void operator=(const CBaseObject& objectSrc); // no implementation
|
||||
|
||||
private:
|
||||
static LONG m_cObjects; /* Total number of objects active */
|
||||
|
||||
protected:
|
||||
#ifdef DEBUG
|
||||
DWORD m_dwCookie; /* Cookie identifying this object */
|
||||
#endif
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/* These increment and decrement the number of active objects */
|
||||
|
||||
CBaseObject(__in_opt LPCTSTR pName);
|
||||
#ifdef UNICODE
|
||||
CBaseObject(__in_opt LPCSTR pName);
|
||||
#endif
|
||||
~CBaseObject();
|
||||
|
||||
/* Call this to find if there are any CUnknown derived objects active */
|
||||
|
||||
static LONG ObjectsActive() {
|
||||
return m_cObjects;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* An object that supports one or more COM interfaces will be based on
|
||||
this class. It supports counting of total objects for DLLCanUnloadNow
|
||||
support, and an implementation of the core non delegating IUnknown */
|
||||
|
||||
class AM_NOVTABLE CUnknown : public INonDelegatingUnknown,
|
||||
public CBaseObject
|
||||
{
|
||||
private:
|
||||
const LPUNKNOWN m_pUnknown; /* Owner of this object */
|
||||
|
||||
protected: /* So we can override NonDelegatingRelease() */
|
||||
volatile LONG m_cRef; /* Number of reference counts */
|
||||
|
||||
public:
|
||||
|
||||
CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk);
|
||||
virtual ~CUnknown() {};
|
||||
|
||||
// This is redundant, just use the other constructor
|
||||
// as we never touch the HRESULT in this anyway
|
||||
CUnknown(__in_opt LPCTSTR Name, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr);
|
||||
#ifdef UNICODE
|
||||
CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk);
|
||||
CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk,__inout_opt HRESULT *phr);
|
||||
#endif
|
||||
|
||||
/* Return the owner of this object */
|
||||
|
||||
LPUNKNOWN GetOwner() const {
|
||||
return m_pUnknown;
|
||||
};
|
||||
|
||||
/* Called from the class factory to create a new instance, it is
|
||||
pure virtual so it must be overriden in your derived class */
|
||||
|
||||
/* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */
|
||||
|
||||
/* Non delegating unknown implementation */
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **);
|
||||
STDMETHODIMP_(ULONG) NonDelegatingAddRef();
|
||||
STDMETHODIMP_(ULONG) NonDelegatingRelease();
|
||||
};
|
||||
|
||||
/* Return an interface pointer to a requesting client
|
||||
performing a thread safe AddRef as necessary */
|
||||
|
||||
STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv);
|
||||
|
||||
/* A function that can create a new COM object */
|
||||
|
||||
typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(__in_opt LPUNKNOWN pUnkOuter, __inout_opt HRESULT *phr);
|
||||
|
||||
/* A function (can be NULL) which is called from the DLL entrypoint
|
||||
routine for each factory template:
|
||||
|
||||
bLoading - TRUE on DLL load, FALSE on DLL unload
|
||||
rclsid - the m_ClsID of the entry
|
||||
*/
|
||||
typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);
|
||||
|
||||
/* Create one of these per object class in an array so that
|
||||
the default class factory code can create new instances */
|
||||
|
||||
class CFactoryTemplate {
|
||||
|
||||
public:
|
||||
|
||||
const WCHAR * m_Name;
|
||||
const CLSID * m_ClsID;
|
||||
LPFNNewCOMObject m_lpfnNew;
|
||||
LPFNInitRoutine m_lpfnInit;
|
||||
const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter;
|
||||
|
||||
BOOL IsClassID(REFCLSID rclsid) const {
|
||||
return (IsEqualCLSID(*m_ClsID,rclsid));
|
||||
};
|
||||
|
||||
CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) const {
|
||||
CheckPointer(phr,NULL);
|
||||
return m_lpfnNew(pUnk, phr);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* You must override the (pure virtual) NonDelegatingQueryInterface to return
|
||||
interface pointers (using GetInterface) to the interfaces your derived
|
||||
class supports (the default implementation only supports IUnknown) */
|
||||
|
||||
#define DECLARE_IUNKNOWN \
|
||||
STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv) { \
|
||||
return GetOwner()->QueryInterface(riid,ppv); \
|
||||
}; \
|
||||
STDMETHODIMP_(ULONG) AddRef() { \
|
||||
return GetOwner()->AddRef(); \
|
||||
}; \
|
||||
STDMETHODIMP_(ULONG) Release() { \
|
||||
return GetOwner()->Release(); \
|
||||
};
|
||||
|
||||
|
||||
|
||||
HINSTANCE LoadOLEAut32();
|
||||
|
||||
|
||||
#endif /* __COMBASE__ */
|
||||
|
||||
|
||||
|
||||
|
||||
+2545
File diff suppressed because it is too large.
Load diff
+923
@@ -0,0 +1,923 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: CtlUtil.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Base classes implementing IDispatch parsing for the basic control dual
|
||||
// interfaces. Derive from these and implement just the custom method and
|
||||
// property methods. We also implement CPosPassThru that can be used by
|
||||
// renderers and transforms to pass by IMediaPosition and IMediaSeeking
|
||||
|
||||
#ifndef __CTLUTIL__
|
||||
#define __CTLUTIL__
|
||||
|
||||
// OLE Automation has different ideas of TRUE and FALSE
|
||||
|
||||
#define OATRUE (-1)
|
||||
#define OAFALSE (0)
|
||||
|
||||
|
||||
// It's possible that we could replace this class with CreateStdDispatch
|
||||
|
||||
class CBaseDispatch
|
||||
{
|
||||
ITypeInfo * m_pti;
|
||||
|
||||
public:
|
||||
|
||||
CBaseDispatch() : m_pti(NULL) {}
|
||||
~CBaseDispatch();
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
REFIID riid,
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
};
|
||||
|
||||
|
||||
class AM_NOVTABLE CMediaControl :
|
||||
public IMediaControl,
|
||||
public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CMediaControl(const TCHAR *, LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
class AM_NOVTABLE CMediaEvent :
|
||||
public IMediaEventEx,
|
||||
public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CMediaEvent(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
class AM_NOVTABLE CMediaPosition :
|
||||
public IMediaPosition,
|
||||
public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
|
||||
CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT *phr);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// OA-compatibility means that we must use double as the RefTime value,
|
||||
// and REFERENCE_TIME (essentially a LONGLONG) within filters.
|
||||
// this class converts between the two
|
||||
|
||||
class COARefTime : public CRefTime {
|
||||
public:
|
||||
|
||||
COARefTime() {
|
||||
};
|
||||
|
||||
COARefTime(CRefTime t)
|
||||
: CRefTime(t)
|
||||
{
|
||||
};
|
||||
|
||||
COARefTime(REFERENCE_TIME t)
|
||||
: CRefTime(t)
|
||||
{
|
||||
};
|
||||
|
||||
COARefTime(double d) {
|
||||
m_time = (LONGLONG) (d * 10000000);
|
||||
};
|
||||
|
||||
operator double() {
|
||||
return double(m_time) / 10000000;
|
||||
};
|
||||
|
||||
operator REFERENCE_TIME() {
|
||||
return m_time;
|
||||
};
|
||||
|
||||
COARefTime& operator=(const double& rd) {
|
||||
m_time = (LONGLONG) (rd * 10000000);
|
||||
return *this;
|
||||
}
|
||||
|
||||
COARefTime& operator=(const REFERENCE_TIME& rt) {
|
||||
m_time = rt;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline BOOL operator==(const COARefTime& rt)
|
||||
{
|
||||
return m_time == rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator!=(const COARefTime& rt)
|
||||
{
|
||||
return m_time != rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator < (const COARefTime& rt)
|
||||
{
|
||||
return m_time < rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator > (const COARefTime& rt)
|
||||
{
|
||||
return m_time > rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator >= (const COARefTime& rt)
|
||||
{
|
||||
return m_time >= rt.m_time;
|
||||
};
|
||||
|
||||
inline BOOL operator <= (const COARefTime& rt)
|
||||
{
|
||||
return m_time <= rt.m_time;
|
||||
};
|
||||
|
||||
inline COARefTime operator+(const COARefTime& rt)
|
||||
{
|
||||
return COARefTime(m_time + rt.m_time);
|
||||
};
|
||||
|
||||
inline COARefTime operator-(const COARefTime& rt)
|
||||
{
|
||||
return COARefTime(m_time - rt.m_time);
|
||||
};
|
||||
|
||||
inline COARefTime operator*(LONG l)
|
||||
{
|
||||
return COARefTime(m_time * l);
|
||||
};
|
||||
|
||||
inline COARefTime operator/(LONG l)
|
||||
{
|
||||
return COARefTime(m_time / l);
|
||||
};
|
||||
|
||||
private:
|
||||
// Prevent bugs from constructing from LONG (which gets
|
||||
// converted to double and then multiplied by 10000000
|
||||
COARefTime(LONG);
|
||||
LONG operator=(LONG);
|
||||
};
|
||||
|
||||
|
||||
// A utility class that handles IMediaPosition and IMediaSeeking on behalf
|
||||
// of single-input pin renderers, or transform filters.
|
||||
//
|
||||
// Renderers will expose this from the filter; transform filters will
|
||||
// expose it from the output pin and not the renderer.
|
||||
//
|
||||
// Create one of these, giving it your IPin* for your input pin, and delegate
|
||||
// all IMediaPosition methods to it. It will query the input pin for
|
||||
// IMediaPosition and respond appropriately.
|
||||
//
|
||||
// Call ForceRefresh if the pin connection changes.
|
||||
//
|
||||
// This class no longer caches the upstream IMediaPosition or IMediaSeeking
|
||||
// it acquires it on each method call. This means ForceRefresh is not needed.
|
||||
// The method is kept for source compatibility and to minimise the changes
|
||||
// if we need to put it back later for performance reasons.
|
||||
|
||||
class CPosPassThru : public IMediaSeeking, public CMediaPosition
|
||||
{
|
||||
IPin *m_pPin;
|
||||
|
||||
HRESULT GetPeer(__deref_out IMediaPosition **ppMP);
|
||||
HRESULT GetPeerSeeking(__deref_out IMediaSeeking **ppMS);
|
||||
|
||||
public:
|
||||
|
||||
CPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *);
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
HRESULT ForceRefresh() {
|
||||
return S_OK;
|
||||
};
|
||||
|
||||
// override to return an accurate current position
|
||||
virtual HRESULT GetMediaTime(__out LONGLONG *pStartTime, __out_opt LONGLONG *pEndTime) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv);
|
||||
|
||||
// IMediaSeeking methods
|
||||
STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities );
|
||||
STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities );
|
||||
STDMETHODIMP SetTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP GetTimeFormat(__out GUID *pFormat);
|
||||
STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP IsFormatSupported( const GUID * pFormat);
|
||||
STDMETHODIMP QueryPreferredFormat( __out GUID *pFormat);
|
||||
STDMETHODIMP ConvertTimeFormat(__out LONGLONG * pTarget,
|
||||
__in_opt const GUID * pTargetFormat,
|
||||
LONGLONG Source,
|
||||
__in_opt const GUID * pSourceFormat );
|
||||
STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent, DWORD CurrentFlags
|
||||
, __inout_opt LONGLONG * pStop, DWORD StopFlags );
|
||||
|
||||
STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop );
|
||||
STDMETHODIMP GetCurrentPosition( __out LONGLONG * pCurrent );
|
||||
STDMETHODIMP GetStopPosition( __out LONGLONG * pStop );
|
||||
STDMETHODIMP SetRate( double dRate);
|
||||
STDMETHODIMP GetRate( __out double * pdRate);
|
||||
STDMETHODIMP GetDuration( __out LONGLONG *pDuration);
|
||||
STDMETHODIMP GetAvailable( __out_opt LONGLONG *pEarliest, __out_opt LONGLONG *pLatest );
|
||||
STDMETHODIMP GetPreroll( __out LONGLONG *pllPreroll );
|
||||
|
||||
// IMediaPosition properties
|
||||
STDMETHODIMP get_Duration(__out REFTIME * plength);
|
||||
STDMETHODIMP put_CurrentPosition(REFTIME llTime);
|
||||
STDMETHODIMP get_StopTime(__out REFTIME * pllTime);
|
||||
STDMETHODIMP put_StopTime(REFTIME llTime);
|
||||
STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime);
|
||||
STDMETHODIMP put_PrerollTime(REFTIME llTime);
|
||||
STDMETHODIMP get_Rate(__out double * pdRate);
|
||||
STDMETHODIMP put_Rate(double dRate);
|
||||
STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime);
|
||||
STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward);
|
||||
STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward);
|
||||
|
||||
private:
|
||||
HRESULT GetSeekingLongLong( HRESULT (__stdcall IMediaSeeking::*pMethod)( LONGLONG * ),
|
||||
__out LONGLONG * pll );
|
||||
};
|
||||
|
||||
|
||||
// Adds the ability to return a current position
|
||||
|
||||
class CRendererPosPassThru : public CPosPassThru
|
||||
{
|
||||
CCritSec m_PositionLock; // Locks access to our position
|
||||
LONGLONG m_StartMedia; // Start media time last seen
|
||||
LONGLONG m_EndMedia; // And likewise the end media
|
||||
BOOL m_bReset; // Have media times been set
|
||||
|
||||
public:
|
||||
|
||||
// Used to help with passing media times through graph
|
||||
|
||||
CRendererPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *);
|
||||
HRESULT RegisterMediaTime(IMediaSample *pMediaSample);
|
||||
HRESULT RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime);
|
||||
HRESULT GetMediaTime(__out LONGLONG *pStartTime,__out_opt LONGLONG *pEndTime);
|
||||
HRESULT ResetMediaTime();
|
||||
HRESULT EOS();
|
||||
};
|
||||
|
||||
STDAPI CreatePosPassThru(
|
||||
__in_opt LPUNKNOWN pAgg,
|
||||
BOOL bRenderer,
|
||||
IPin *pPin,
|
||||
__deref_out IUnknown **ppPassThru
|
||||
);
|
||||
|
||||
// A class that handles the IDispatch part of IBasicAudio and leaves the
|
||||
// properties and methods themselves pure virtual.
|
||||
|
||||
class AM_NOVTABLE CBasicAudio : public IBasicAudio, public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CBasicAudio(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
// A class that handles the IDispatch part of IBasicVideo and leaves the
|
||||
// properties and methods themselves pure virtual.
|
||||
|
||||
class AM_NOVTABLE CBaseBasicVideo : public IBasicVideo2, public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CBaseBasicVideo(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
|
||||
STDMETHODIMP GetPreferredAspectRatio(
|
||||
__out long *plAspectX,
|
||||
__out long *plAspectY)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// A class that handles the IDispatch part of IVideoWindow and leaves the
|
||||
// properties and methods themselves pure virtual.
|
||||
|
||||
class AM_NOVTABLE CBaseVideoWindow : public IVideoWindow, public CUnknown
|
||||
{
|
||||
CBaseDispatch m_basedisp;
|
||||
|
||||
public:
|
||||
|
||||
CBaseVideoWindow(__in_opt LPCTSTR, __in_opt LPUNKNOWN);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
/* IDispatch methods */
|
||||
STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
|
||||
|
||||
STDMETHODIMP GetTypeInfo(
|
||||
UINT itinfo,
|
||||
LCID lcid,
|
||||
__deref_out ITypeInfo ** pptinfo);
|
||||
|
||||
STDMETHODIMP GetIDsOfNames(
|
||||
REFIID riid,
|
||||
__in_ecount(cNames) LPOLESTR * rgszNames,
|
||||
UINT cNames,
|
||||
LCID lcid,
|
||||
__out_ecount(cNames) DISPID * rgdispid);
|
||||
|
||||
STDMETHODIMP Invoke(
|
||||
DISPID dispidMember,
|
||||
REFIID riid,
|
||||
LCID lcid,
|
||||
WORD wFlags,
|
||||
__in DISPPARAMS * pdispparams,
|
||||
__out_opt VARIANT * pvarResult,
|
||||
__out_opt EXCEPINFO * pexcepinfo,
|
||||
__out_opt UINT * puArgErr);
|
||||
};
|
||||
|
||||
|
||||
// abstract class to help source filters with their implementation
|
||||
// of IMediaPosition. Derive from this and set the duration (and stop
|
||||
// position). Also override NotifyChange to do something when the properties
|
||||
// change.
|
||||
|
||||
class AM_NOVTABLE CSourcePosition : public CMediaPosition
|
||||
{
|
||||
|
||||
public:
|
||||
CSourcePosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *);
|
||||
|
||||
// IMediaPosition methods
|
||||
STDMETHODIMP get_Duration(__out REFTIME * plength);
|
||||
STDMETHODIMP put_CurrentPosition(REFTIME llTime);
|
||||
STDMETHODIMP get_StopTime(__out REFTIME * pllTime);
|
||||
STDMETHODIMP put_StopTime(REFTIME llTime);
|
||||
STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime);
|
||||
STDMETHODIMP put_PrerollTime(REFTIME llTime);
|
||||
STDMETHODIMP get_Rate(__out double * pdRate);
|
||||
STDMETHODIMP put_Rate(double dRate);
|
||||
STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward);
|
||||
STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward);
|
||||
|
||||
// override if you can return the data you are actually working on
|
||||
STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime) {
|
||||
return E_NOTIMPL;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
// we call this to notify changes. Override to handle them
|
||||
virtual HRESULT ChangeStart() PURE;
|
||||
virtual HRESULT ChangeStop() PURE;
|
||||
virtual HRESULT ChangeRate() PURE;
|
||||
|
||||
COARefTime m_Duration;
|
||||
COARefTime m_Start;
|
||||
COARefTime m_Stop;
|
||||
double m_Rate;
|
||||
|
||||
CCritSec * m_pLock;
|
||||
};
|
||||
|
||||
class AM_NOVTABLE CSourceSeeking :
|
||||
public IMediaSeeking,
|
||||
public CUnknown
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
DECLARE_IUNKNOWN;
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
|
||||
|
||||
// IMediaSeeking methods
|
||||
|
||||
STDMETHODIMP IsFormatSupported(const GUID * pFormat);
|
||||
STDMETHODIMP QueryPreferredFormat(__out GUID *pFormat);
|
||||
STDMETHODIMP SetTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);
|
||||
STDMETHODIMP GetTimeFormat(__out GUID *pFormat);
|
||||
STDMETHODIMP GetDuration(__out LONGLONG *pDuration);
|
||||
STDMETHODIMP GetStopPosition(__out LONGLONG *pStop);
|
||||
STDMETHODIMP GetCurrentPosition(__out LONGLONG *pCurrent);
|
||||
STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities );
|
||||
STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities );
|
||||
STDMETHODIMP ConvertTimeFormat( __out LONGLONG * pTarget,
|
||||
__in_opt const GUID * pTargetFormat,
|
||||
LONGLONG Source,
|
||||
__in_opt const GUID * pSourceFormat );
|
||||
|
||||
STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent, DWORD CurrentFlags
|
||||
, __inout_opt LONGLONG * pStop, DWORD StopFlags );
|
||||
|
||||
STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop );
|
||||
|
||||
STDMETHODIMP GetAvailable( __out_opt LONGLONG * pEarliest, __out_opt LONGLONG * pLatest );
|
||||
STDMETHODIMP SetRate( double dRate);
|
||||
STDMETHODIMP GetRate( __out double * pdRate);
|
||||
STDMETHODIMP GetPreroll(__out LONGLONG *pPreroll);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// ctor
|
||||
CSourceSeeking(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *);
|
||||
|
||||
// we call this to notify changes. Override to handle them
|
||||
virtual HRESULT ChangeStart() PURE;
|
||||
virtual HRESULT ChangeStop() PURE;
|
||||
virtual HRESULT ChangeRate() PURE;
|
||||
|
||||
CRefTime m_rtDuration; // length of stream
|
||||
CRefTime m_rtStart; // source will start here
|
||||
CRefTime m_rtStop; // source will stop here
|
||||
double m_dRateSeeking;
|
||||
|
||||
// seeking capabilities
|
||||
DWORD m_dwSeekingCaps;
|
||||
|
||||
CCritSec * m_pLock;
|
||||
};
|
||||
|
||||
|
||||
// Base classes supporting Deferred commands.
|
||||
|
||||
// Deferred commands are queued by calls to methods on the IQueueCommand
|
||||
// interface, exposed by the filtergraph and by some filters. A successful
|
||||
// call to one of these methods will return an IDeferredCommand interface
|
||||
// representing the queued command.
|
||||
//
|
||||
// A CDeferredCommand object represents a single deferred command, and exposes
|
||||
// the IDeferredCommand interface as well as other methods permitting time
|
||||
// checks and actual execution. It contains a reference to the CCommandQueue
|
||||
// object on which it is queued.
|
||||
//
|
||||
// CCommandQueue is a base class providing a queue of CDeferredCommand
|
||||
// objects, and methods to add, remove, check status and invoke the queued
|
||||
// commands. A CCommandQueue object would be part of an object that
|
||||
// implemented IQueueCommand.
|
||||
|
||||
class CCmdQueue;
|
||||
|
||||
// take a copy of the params and store them. Release any allocated
|
||||
// memory in destructor
|
||||
|
||||
class CDispParams : public DISPPARAMS
|
||||
{
|
||||
public:
|
||||
CDispParams(UINT nArgs, __in_ecount(nArgs) VARIANT* pArgs, __inout_opt HRESULT *phr = NULL);
|
||||
~CDispParams();
|
||||
};
|
||||
|
||||
|
||||
// CDeferredCommand lifetime is controlled by refcounts. Caller of
|
||||
// InvokeAt.. gets a refcounted interface pointer, and the CCmdQueue
|
||||
// object also holds a refcount on us. Calling Cancel or Invoke takes
|
||||
// us off the CCmdQueue and thus reduces the refcount by 1. Once taken
|
||||
// off the queue we cannot be put back on the queue.
|
||||
|
||||
class CDeferredCommand
|
||||
: public CUnknown,
|
||||
public IDeferredCommand
|
||||
{
|
||||
public:
|
||||
|
||||
CDeferredCommand(
|
||||
__inout CCmdQueue * pQ,
|
||||
__in_opt LPUNKNOWN pUnk, // aggregation outer unk
|
||||
__inout HRESULT * phr,
|
||||
__in LPUNKNOWN pUnkExecutor, // object that will execute this cmd
|
||||
REFTIME time,
|
||||
__in GUID* iid,
|
||||
long dispidMethod,
|
||||
short wFlags,
|
||||
long cArgs,
|
||||
__in_ecount(cArgs) VARIANT* pDispParams,
|
||||
__out VARIANT* pvarResult,
|
||||
__out short* puArgErr,
|
||||
BOOL bStream
|
||||
);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
// override this to publicise our interfaces
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __out void **ppv);
|
||||
|
||||
// IDeferredCommand methods
|
||||
STDMETHODIMP Cancel();
|
||||
STDMETHODIMP Confidence(
|
||||
__out LONG* pConfidence);
|
||||
STDMETHODIMP Postpone(
|
||||
REFTIME newtime);
|
||||
STDMETHODIMP GetHResult(
|
||||
__out HRESULT* phrResult);
|
||||
|
||||
// other public methods
|
||||
|
||||
HRESULT Invoke();
|
||||
|
||||
// access methods
|
||||
|
||||
// returns TRUE if streamtime, FALSE if presentation time
|
||||
BOOL IsStreamTime() {
|
||||
return m_bStream;
|
||||
};
|
||||
|
||||
CRefTime GetTime() {
|
||||
return m_time;
|
||||
};
|
||||
|
||||
REFIID GetIID() {
|
||||
return *m_iid;
|
||||
};
|
||||
|
||||
long GetMethod() {
|
||||
return m_dispidMethod;
|
||||
};
|
||||
|
||||
short GetFlags() {
|
||||
return m_wFlags;
|
||||
};
|
||||
|
||||
DISPPARAMS* GetParams() {
|
||||
return &m_DispParams;
|
||||
};
|
||||
|
||||
VARIANT* GetResult() {
|
||||
return m_pvarResult;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
CCmdQueue* m_pQueue;
|
||||
|
||||
// pUnk for the interface that we will execute the command on
|
||||
LPUNKNOWN m_pUnk;
|
||||
|
||||
// stored command data
|
||||
REFERENCE_TIME m_time;
|
||||
GUID* m_iid;
|
||||
long m_dispidMethod;
|
||||
short m_wFlags;
|
||||
VARIANT* m_pvarResult;
|
||||
BOOL m_bStream;
|
||||
CDispParams m_DispParams;
|
||||
DISPID m_DispId; // For get and put
|
||||
|
||||
// we use this for ITypeInfo access
|
||||
CBaseDispatch m_Dispatch;
|
||||
|
||||
// save retval here
|
||||
HRESULT m_hrResult;
|
||||
};
|
||||
|
||||
|
||||
// a list of CDeferredCommand objects. this is a base class providing
|
||||
// the basics of access to the list. If you want to use CDeferredCommand
|
||||
// objects then your queue needs to be derived from this class.
|
||||
|
||||
class AM_NOVTABLE CCmdQueue
|
||||
{
|
||||
public:
|
||||
CCmdQueue(__inout_opt HRESULT *phr = NULL);
|
||||
virtual ~CCmdQueue();
|
||||
|
||||
// returns a new CDeferredCommand object that will be initialised with
|
||||
// the parameters and will be added to the queue during construction.
|
||||
// returns S_OK if successfully created otherwise an error and
|
||||
// no object has been queued.
|
||||
virtual HRESULT New(
|
||||
__out CDeferredCommand **ppCmd,
|
||||
__in LPUNKNOWN pUnk,
|
||||
REFTIME time,
|
||||
__in GUID* iid,
|
||||
long dispidMethod,
|
||||
short wFlags,
|
||||
long cArgs,
|
||||
__in_ecount(cArgs) VARIANT* pDispParams,
|
||||
__out VARIANT* pvarResult,
|
||||
__out short* puArgErr,
|
||||
BOOL bStream
|
||||
);
|
||||
|
||||
// called by the CDeferredCommand object to add and remove itself
|
||||
// from the queue
|
||||
virtual HRESULT Insert(__in CDeferredCommand* pCmd);
|
||||
virtual HRESULT Remove(__in CDeferredCommand* pCmd);
|
||||
|
||||
// Command-Due Checking
|
||||
//
|
||||
// There are two schemes of synchronisation: coarse and accurate. In
|
||||
// coarse mode, you wait till the time arrives and then execute the cmd.
|
||||
// In accurate mode, you wait until you are processing the sample that
|
||||
// will appear at the time, and then execute the command. It's up to the
|
||||
// filter which one it will implement. The filtergraph will always
|
||||
// implement coarse mode for commands queued at the filtergraph.
|
||||
//
|
||||
// If you want coarse sync, you probably want to wait until there is a
|
||||
// command due, and then execute it. You can do this by calling
|
||||
// GetDueCommand. If you have several things to wait for, get the
|
||||
// event handle from GetDueHandle() and when this is signalled then call
|
||||
// GetDueCommand. Stream time will only advance between calls to Run and
|
||||
// EndRun. Note that to avoid an extra thread there is no guarantee that
|
||||
// if the handle is set there will be a command ready. Each time the
|
||||
// event is signalled, call GetDueCommand (probably with a 0 timeout);
|
||||
// This may return E_ABORT.
|
||||
//
|
||||
// If you want accurate sync, you must call GetCommandDueFor, passing
|
||||
// as a parameter the stream time of the samples you are about to process.
|
||||
// This will return:
|
||||
// -- a stream-time command due at or before that stream time
|
||||
// -- a presentation-time command due at or before the
|
||||
// time that stream time will be presented (only between Run
|
||||
// and EndRun calls, since outside of this, the mapping from
|
||||
// stream time to presentation time is not known.
|
||||
// -- any presentation-time command due now.
|
||||
// This means that if you want accurate synchronisation on samples that
|
||||
// might be processed during Paused mode, you need to use
|
||||
// stream-time commands.
|
||||
//
|
||||
// In all cases, commands remain queued until Invoked or Cancelled. The
|
||||
// setting and resetting of the event handle is managed entirely by this
|
||||
// queue object.
|
||||
|
||||
// set the clock used for timing
|
||||
virtual HRESULT SetSyncSource(__in_opt IReferenceClock*);
|
||||
|
||||
// switch to run mode. Streamtime to Presentation time mapping known.
|
||||
virtual HRESULT Run(REFERENCE_TIME tStreamTimeOffset);
|
||||
|
||||
// switch to Stopped or Paused mode. Time mapping not known.
|
||||
virtual HRESULT EndRun();
|
||||
|
||||
// return a pointer to the next due command. Blocks for msTimeout
|
||||
// milliseconds until there is a due command.
|
||||
// Stream-time commands will only become due between Run and Endrun calls.
|
||||
// The command remains queued until invoked or cancelled.
|
||||
// Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).
|
||||
// Returns an AddRef-ed object
|
||||
virtual HRESULT GetDueCommand(__out CDeferredCommand ** ppCmd, long msTimeout);
|
||||
|
||||
// return the event handle that will be signalled whenever
|
||||
// there are deferred commands due for execution (when GetDueCommand
|
||||
// will not block).
|
||||
HANDLE GetDueHandle() {
|
||||
return HANDLE(m_evDue);
|
||||
};
|
||||
|
||||
// return a pointer to a command that will be due for a given time.
|
||||
// Pass in a stream time here. The stream time offset will be passed
|
||||
// in via the Run method.
|
||||
// Commands remain queued until invoked or cancelled.
|
||||
// This method will not block. It will report VFW_E_NOT_FOUND if there
|
||||
// are no commands due yet.
|
||||
// Returns an AddRef-ed object
|
||||
virtual HRESULT GetCommandDueFor(REFERENCE_TIME tStream, __out CDeferredCommand**ppCmd);
|
||||
|
||||
// check if a given time is due (TRUE if it is due yet)
|
||||
BOOL CheckTime(CRefTime time, BOOL bStream) {
|
||||
|
||||
// if no clock, nothing is due!
|
||||
if (!m_pClock) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// stream time
|
||||
if (bStream) {
|
||||
|
||||
// not valid if not running
|
||||
if (!m_bRunning) {
|
||||
return FALSE;
|
||||
}
|
||||
// add on known stream time offset to get presentation time
|
||||
time += m_StreamTimeOffset;
|
||||
}
|
||||
|
||||
CRefTime Now;
|
||||
m_pClock->GetTime((REFERENCE_TIME*)&Now);
|
||||
return (time <= Now);
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
// protect access to lists etc
|
||||
CCritSec m_Lock;
|
||||
|
||||
// commands queued in presentation time are stored here
|
||||
CGenericList<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__
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: FourCC.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// FOURCCMap
|
||||
//
|
||||
// provides a mapping between old-style multimedia format DWORDs
|
||||
// and new-style GUIDs.
|
||||
//
|
||||
// A range of 4 billion GUIDs has been allocated to ensure that this
|
||||
// mapping can be done straightforwardly one-to-one in both directions.
|
||||
//
|
||||
// January 95
|
||||
|
||||
|
||||
#ifndef __FOURCC__
|
||||
#define __FOURCC__
|
||||
|
||||
|
||||
// Multimedia format types are marked with DWORDs built from four 8-bit
|
||||
// chars and known as FOURCCs. New multimedia AM_MEDIA_TYPE definitions include
|
||||
// a subtype GUID. In order to simplify the mapping, GUIDs in the range:
|
||||
// XXXXXXXX-0000-0010-8000-00AA00389B71
|
||||
// are reserved for FOURCCs.
|
||||
|
||||
class FOURCCMap : public GUID
|
||||
{
|
||||
|
||||
public:
|
||||
FOURCCMap();
|
||||
FOURCCMap(DWORD Fourcc);
|
||||
FOURCCMap(const GUID *);
|
||||
|
||||
|
||||
DWORD GetFOURCC(void);
|
||||
void SetFOURCC(DWORD fourcc);
|
||||
void SetFOURCC(const GUID *);
|
||||
|
||||
private:
|
||||
void InitGUID();
|
||||
};
|
||||
|
||||
#define GUID_Data2 0
|
||||
#define GUID_Data3 0x10
|
||||
#define GUID_Data4_1 0xaa000080
|
||||
#define GUID_Data4_2 0x719b3800
|
||||
|
||||
inline void
|
||||
FOURCCMap::InitGUID() {
|
||||
Data2 = GUID_Data2;
|
||||
Data3 = GUID_Data3;
|
||||
((DWORD *)Data4)[0] = GUID_Data4_1;
|
||||
((DWORD *)Data4)[1] = GUID_Data4_2;
|
||||
}
|
||||
|
||||
inline
|
||||
FOURCCMap::FOURCCMap() {
|
||||
InitGUID();
|
||||
SetFOURCC( DWORD(0));
|
||||
}
|
||||
|
||||
inline
|
||||
FOURCCMap::FOURCCMap(DWORD fourcc)
|
||||
{
|
||||
InitGUID();
|
||||
SetFOURCC(fourcc);
|
||||
}
|
||||
|
||||
inline
|
||||
FOURCCMap::FOURCCMap(const GUID * pGuid)
|
||||
{
|
||||
InitGUID();
|
||||
SetFOURCC(pGuid);
|
||||
}
|
||||
|
||||
inline void
|
||||
FOURCCMap::SetFOURCC(const GUID * pGuid)
|
||||
{
|
||||
FOURCCMap * p = (FOURCCMap*) pGuid;
|
||||
SetFOURCC(p->GetFOURCC());
|
||||
}
|
||||
|
||||
inline void
|
||||
FOURCCMap::SetFOURCC(DWORD fourcc)
|
||||
{
|
||||
Data1 = fourcc;
|
||||
}
|
||||
|
||||
inline DWORD
|
||||
FOURCCMap::GetFOURCC(void)
|
||||
{
|
||||
return Data1;
|
||||
}
|
||||
|
||||
#endif /* __FOURCC__ */
|
||||
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Measure.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*
|
||||
The idea is to pepper the source code with interesting measurements and
|
||||
have the last few thousand of these recorded in a circular buffer that
|
||||
can be post-processed to give interesting numbers.
|
||||
|
||||
WHAT THE LOG LOOKS LIKE:
|
||||
|
||||
Time (sec) Type Delta Incident_Name
|
||||
0.055,41 NOTE -. Incident Nine - Another note
|
||||
0.055,42 NOTE 0.000,01 Incident Nine - Another note
|
||||
0.055,44 NOTE 0.000,02 Incident Nine - Another note
|
||||
0.055,45 STOP -. Incident Eight - Also random
|
||||
0.055,47 START -. Incident Seven - Random
|
||||
0.055,49 NOTE 0.000,05 Incident Nine - Another note
|
||||
------- <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(__in LPTSTR Incident);
|
||||
|
||||
|
||||
// Reset the statistical counts for an incident
|
||||
|
||||
void WINAPI Msr_Reset(int Id);
|
||||
|
||||
|
||||
// Reset all the counts for all incidents
|
||||
#define MSR_RESET_ALL 0
|
||||
#define MSR_PAUSE 1
|
||||
#define MSR_RUN 2
|
||||
|
||||
void WINAPI Msr_Control(int iAction);
|
||||
|
||||
|
||||
// log the start of an operation
|
||||
|
||||
void WINAPI Msr_Start(int Id);
|
||||
|
||||
|
||||
// log the end of an operation
|
||||
|
||||
void WINAPI Msr_Stop(int Id);
|
||||
|
||||
|
||||
// log a one-off or repetitive operation
|
||||
|
||||
void WINAPI Msr_Note(int Id);
|
||||
|
||||
|
||||
// log an integer (on which we can see statistics later)
|
||||
void WINAPI Msr_Integer(int Id, int n);
|
||||
|
||||
|
||||
// print out all the vaialable log (it may have wrapped) and then the statistics.
|
||||
// When the log wraps you lose log but the statistics are still complete.
|
||||
// hFIle==NULL => use DbgLog
|
||||
// otherwise hFile must have come from CreateFile or OpenFile.
|
||||
|
||||
void WINAPI Msr_Dump(HANDLE hFile);
|
||||
|
||||
|
||||
// just dump the statistics - never mind the log
|
||||
|
||||
void WINAPI Msr_DumpStats(HANDLE hFile);
|
||||
|
||||
// Type definitions in case you want to declare a pointer to the dump functions
|
||||
// (makes it a trifle easier to do dynamic linking
|
||||
// i.e. LoadModule, GetProcAddress and call that)
|
||||
|
||||
// Typedefs so can declare MSR_DUMPPROC *MsrDumpStats; or whatever
|
||||
typedef void WINAPI MSR_DUMPPROC(HANDLE hFile);
|
||||
typedef void WINAPI MSR_CONTROLPROC(int iAction);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __MEASURE__
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: MsgThrd.h
|
||||
//
|
||||
// Desc: DirectShow base classes - provides support for a worker thread
|
||||
// class to which one can asynchronously post messages.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Message class - really just a structure.
|
||||
//
|
||||
class CMsg {
|
||||
public:
|
||||
UINT uMsg;
|
||||
DWORD dwFlags;
|
||||
LPVOID lpParam;
|
||||
CAMEvent *pEvent;
|
||||
|
||||
CMsg(UINT u, DWORD dw, __inout_opt LPVOID lp, __in_opt CAMEvent *pEvnt)
|
||||
: uMsg(u), dwFlags(dw), lpParam(lp), pEvent(pEvnt) {}
|
||||
|
||||
CMsg()
|
||||
: uMsg(0), dwFlags(0L), lpParam(NULL), pEvent(NULL) {}
|
||||
};
|
||||
|
||||
// This is the actual thread class. It exports all the usual thread control
|
||||
// functions. The created thread is different from a normal WIN32 thread in
|
||||
// that it is prompted to perform particaular tasks by responding to messages
|
||||
// posted to its message queue.
|
||||
//
|
||||
class AM_NOVTABLE CMsgThread {
|
||||
private:
|
||||
static DWORD WINAPI DefaultThreadProc(__inout LPVOID lpParam);
|
||||
DWORD m_ThreadId;
|
||||
HANDLE m_hThread;
|
||||
|
||||
protected:
|
||||
|
||||
// if you want to override GetThreadMsg to block on other things
|
||||
// as well as this queue, you need access to this
|
||||
CGenericList<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(__out CMsg *msg);
|
||||
|
||||
// override this if you want to do something on thread startup
|
||||
virtual void OnThreadInit() {
|
||||
};
|
||||
|
||||
BOOL CreateThread();
|
||||
|
||||
BOOL WaitForThreadExit(__out LPDWORD lpdwExitCode) {
|
||||
if (m_hThread != NULL) {
|
||||
WaitForSingleObject(m_hThread, INFINITE);
|
||||
return GetExitCodeThread(m_hThread, lpdwExitCode);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD ResumeThread() {
|
||||
return ::ResumeThread(m_hThread);
|
||||
}
|
||||
|
||||
DWORD SuspendThread() {
|
||||
return ::SuspendThread(m_hThread);
|
||||
}
|
||||
|
||||
int GetThreadPriority() {
|
||||
return ::GetThreadPriority(m_hThread);
|
||||
}
|
||||
|
||||
BOOL SetThreadPriority(int nPriority) {
|
||||
return ::SetThreadPriority(m_hThread, nPriority);
|
||||
}
|
||||
|
||||
HANDLE GetThreadHandle() {
|
||||
return m_hThread;
|
||||
}
|
||||
|
||||
DWORD GetThreadId() {
|
||||
return m_ThreadId;
|
||||
}
|
||||
|
||||
|
||||
void PutThreadMsg(UINT uMsg, DWORD dwMsgFlags,
|
||||
__in_opt LPVOID lpMsgParam, __in_opt CAMEvent *pEvent = NULL) {
|
||||
CAutoLock lck(&m_Lock);
|
||||
CMsg* pMsg = new CMsg(uMsg, dwMsgFlags, lpMsgParam, pEvent);
|
||||
m_ThreadQueue.AddTail(pMsg);
|
||||
if (m_lWaiting != 0) {
|
||||
ReleaseSemaphore(m_hSem, m_lWaiting, 0);
|
||||
m_lWaiting = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// This is the function prototype of the function that the client
|
||||
// supplies. It is always called on the created thread, never on
|
||||
// the creator thread.
|
||||
//
|
||||
virtual LRESULT ThreadMessageProc(
|
||||
UINT uMsg, DWORD dwFlags, __inout_opt LPVOID lpParam, __in_opt CAMEvent *pEvent) = 0;
|
||||
};
|
||||
|
||||
+483
@@ -0,0 +1,483 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: MType.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements a class that holds and
|
||||
// manages media type information.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <pjmedia-videodev/config.h>
|
||||
|
||||
#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
|
||||
|
||||
// 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, __out_opt HRESULT* phr)
|
||||
{
|
||||
HRESULT hr = CopyMediaType(this, &rt);
|
||||
if (FAILED(hr) && (NULL != phr)) {
|
||||
*phr = hr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CMediaType::CMediaType(const CMediaType& rt, __out_opt HRESULT* phr)
|
||||
{
|
||||
HRESULT hr = CopyMediaType(this, &rt);
|
||||
if (FAILED(hr) && (NULL != phr)) {
|
||||
*phr = hr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// this class inherits publicly from AM_MEDIA_TYPE so the compiler could generate
|
||||
// the following assignment operator itself, however it could introduce some
|
||||
// memory conflicts and leaks in the process because the structure contains
|
||||
// a dynamically allocated block (pbFormat) which it will not copy correctly
|
||||
|
||||
CMediaType&
|
||||
CMediaType::operator=(const AM_MEDIA_TYPE& rt)
|
||||
{
|
||||
Set(rt);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CMediaType&
|
||||
CMediaType::operator=(const CMediaType& rt)
|
||||
{
|
||||
*this = (AM_MEDIA_TYPE &) rt;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BOOL
|
||||
CMediaType::operator == (const CMediaType& rt) const
|
||||
{
|
||||
// I don't believe we need to check sample size or
|
||||
// temporal compression flags, since I think these must
|
||||
// be represented in the type, subtype and format somehow. They
|
||||
// are pulled out as separate flags so that people who don't understand
|
||||
// the particular format representation can still see them, but
|
||||
// they should duplicate information in the format block.
|
||||
|
||||
return ((IsEqualGUID(majortype,rt.majortype) == TRUE) &&
|
||||
(IsEqualGUID(subtype,rt.subtype) == TRUE) &&
|
||||
(IsEqualGUID(formattype,rt.formattype) == TRUE) &&
|
||||
(cbFormat == rt.cbFormat) &&
|
||||
( (cbFormat == 0) ||
|
||||
pbFormat != NULL && rt.pbFormat != NULL &&
|
||||
(memcmp(pbFormat, rt.pbFormat, cbFormat) == 0)));
|
||||
}
|
||||
|
||||
|
||||
BOOL
|
||||
CMediaType::operator != (const CMediaType& rt) const
|
||||
{
|
||||
/* Check to see if they are equal */
|
||||
|
||||
if (*this == rt) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
CMediaType::Set(const CMediaType& rt)
|
||||
{
|
||||
return Set((AM_MEDIA_TYPE &) rt);
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
CMediaType::Set(const AM_MEDIA_TYPE& rt)
|
||||
{
|
||||
if (&rt != this) {
|
||||
FreeMediaType(*this);
|
||||
HRESULT hr = CopyMediaType(this, &rt);
|
||||
if (FAILED(hr)) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
BOOL
|
||||
CMediaType::IsValid() const
|
||||
{
|
||||
return (!IsEqualGUID(majortype,GUID_NULL));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetType(const GUID* ptype)
|
||||
{
|
||||
majortype = *ptype;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetSubtype(const GUID* ptype)
|
||||
{
|
||||
subtype = *ptype;
|
||||
}
|
||||
|
||||
|
||||
ULONG
|
||||
CMediaType::GetSampleSize() const {
|
||||
if (IsFixedSize()) {
|
||||
return lSampleSize;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetSampleSize(ULONG sz) {
|
||||
if (sz == 0) {
|
||||
SetVariableSize();
|
||||
} else {
|
||||
bFixedSizeSamples = TRUE;
|
||||
lSampleSize = sz;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetVariableSize() {
|
||||
bFixedSizeSamples = FALSE;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CMediaType::SetTemporalCompression(BOOL bCompressed) {
|
||||
bTemporalCompression = bCompressed;
|
||||
}
|
||||
|
||||
BOOL
|
||||
CMediaType::SetFormat(__in_bcount(cb) BYTE * pformat, ULONG cb)
|
||||
{
|
||||
if (NULL == AllocFormatBuffer(cb))
|
||||
return(FALSE);
|
||||
|
||||
ASSERT(pbFormat);
|
||||
memcpy(pbFormat, pformat, cb);
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
|
||||
// set the type of the media type format block, this type defines what you
|
||||
// will actually find in the format pointer. For example FORMAT_VideoInfo or
|
||||
// FORMAT_WaveFormatEx. In the future this may be an interface pointer to a
|
||||
// property set. Before sending out media types this should be filled in.
|
||||
|
||||
void
|
||||
CMediaType::SetFormatType(const GUID *pformattype)
|
||||
{
|
||||
formattype = *pformattype;
|
||||
}
|
||||
|
||||
|
||||
// reset the format buffer
|
||||
|
||||
void CMediaType::ResetFormatBuffer()
|
||||
{
|
||||
if (cbFormat) {
|
||||
CoTaskMemFree((PVOID)pbFormat);
|
||||
}
|
||||
cbFormat = 0;
|
||||
pbFormat = NULL;
|
||||
}
|
||||
|
||||
|
||||
// allocate length bytes for the format and return a read/write pointer
|
||||
// If we cannot allocate the new block of memory we return NULL leaving
|
||||
// the original block of memory untouched (as does ReallocFormatBuffer)
|
||||
|
||||
BYTE*
|
||||
CMediaType::AllocFormatBuffer(ULONG length)
|
||||
{
|
||||
ASSERT(length);
|
||||
|
||||
// do the types have the same buffer size
|
||||
|
||||
if (cbFormat == length) {
|
||||
return pbFormat;
|
||||
}
|
||||
|
||||
// allocate the new format buffer
|
||||
|
||||
BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);
|
||||
if (pNewFormat == NULL) {
|
||||
if (length <= cbFormat) return pbFormat; //reuse the old block anyway.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// delete the old format
|
||||
|
||||
if (cbFormat != 0) {
|
||||
ASSERT(pbFormat);
|
||||
CoTaskMemFree((PVOID)pbFormat);
|
||||
}
|
||||
|
||||
cbFormat = length;
|
||||
pbFormat = pNewFormat;
|
||||
return pbFormat;
|
||||
}
|
||||
|
||||
|
||||
// reallocate length bytes for the format and return a read/write pointer
|
||||
// to it. We keep as much information as we can given the new buffer size
|
||||
// if this fails the original format buffer is left untouched. The caller
|
||||
// is responsible for ensuring the size of memory required is non zero
|
||||
|
||||
BYTE*
|
||||
CMediaType::ReallocFormatBuffer(ULONG length)
|
||||
{
|
||||
ASSERT(length);
|
||||
|
||||
// do the types have the same buffer size
|
||||
|
||||
if (cbFormat == length) {
|
||||
return pbFormat;
|
||||
}
|
||||
|
||||
// allocate the new format buffer
|
||||
|
||||
BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);
|
||||
if (pNewFormat == NULL) {
|
||||
if (length <= cbFormat) return pbFormat; //reuse the old block anyway.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// copy any previous format (or part of if new is smaller)
|
||||
// delete the old format and replace with the new one
|
||||
|
||||
if (cbFormat != 0) {
|
||||
ASSERT(pbFormat);
|
||||
memcpy(pNewFormat,pbFormat,min(length,cbFormat));
|
||||
CoTaskMemFree((PVOID)pbFormat);
|
||||
}
|
||||
|
||||
cbFormat = length;
|
||||
pbFormat = pNewFormat;
|
||||
return pNewFormat;
|
||||
}
|
||||
|
||||
// initialise a media type structure
|
||||
|
||||
void CMediaType::InitMediaType()
|
||||
{
|
||||
ZeroMemory((PVOID)this, sizeof(*this));
|
||||
lSampleSize = 1;
|
||||
bFixedSizeSamples = TRUE;
|
||||
}
|
||||
|
||||
|
||||
// a partially specified media type can be passed to IPin::Connect
|
||||
// as a constraint on the media type used in the connection.
|
||||
// the type, subtype or format type can be null.
|
||||
BOOL
|
||||
CMediaType::IsPartiallySpecified(void) const
|
||||
{
|
||||
if ((majortype == GUID_NULL) ||
|
||||
(formattype == GUID_NULL)) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL
|
||||
CMediaType::MatchesPartial(const CMediaType* ppartial) const
|
||||
{
|
||||
if ((ppartial->majortype != GUID_NULL) &&
|
||||
(majortype != ppartial->majortype)) {
|
||||
return FALSE;
|
||||
}
|
||||
if ((ppartial->subtype != GUID_NULL) &&
|
||||
(subtype != ppartial->subtype)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ppartial->formattype != GUID_NULL) {
|
||||
// if the format block is specified then it must match exactly
|
||||
if (formattype != ppartial->formattype) {
|
||||
return FALSE;
|
||||
}
|
||||
if (cbFormat != ppartial->cbFormat) {
|
||||
return FALSE;
|
||||
}
|
||||
if ((cbFormat != 0) &&
|
||||
(memcmp(pbFormat, ppartial->pbFormat, cbFormat) != 0)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// general purpose function to delete a heap allocated AM_MEDIA_TYPE structure
|
||||
// which is useful when calling IEnumMediaTypes::Next as the interface
|
||||
// implementation allocates the structures which you must later delete
|
||||
// the format block may also be a pointer to an interface to release
|
||||
|
||||
void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt)
|
||||
{
|
||||
// allow NULL pointers for coding simplicity
|
||||
|
||||
if (pmt == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
FreeMediaType(*pmt);
|
||||
CoTaskMemFree((PVOID)pmt);
|
||||
}
|
||||
|
||||
|
||||
// this also comes in useful when using the IEnumMediaTypes interface so
|
||||
// that you can copy a media type, you can do nearly the same by creating
|
||||
// a CMediaType object but as soon as it goes out of scope the destructor
|
||||
// will delete the memory it allocated (this takes a copy of the memory)
|
||||
|
||||
AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc)
|
||||
{
|
||||
ASSERT(pSrc);
|
||||
|
||||
// Allocate a block of memory for the media type
|
||||
|
||||
AM_MEDIA_TYPE *pMediaType =
|
||||
(AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
|
||||
|
||||
if (pMediaType == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Copy the variable length format block
|
||||
|
||||
HRESULT hr = CopyMediaType(pMediaType,pSrc);
|
||||
if (FAILED(hr)) {
|
||||
CoTaskMemFree((PVOID)pMediaType);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pMediaType;
|
||||
}
|
||||
|
||||
|
||||
// Copy 1 media type to another
|
||||
|
||||
HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource)
|
||||
{
|
||||
// We'll leak if we copy onto one that already exists - there's one
|
||||
// case we can check like that - copying to itself.
|
||||
ASSERT(pmtSource != pmtTarget);
|
||||
*pmtTarget = *pmtSource;
|
||||
if (pmtSource->cbFormat != 0) {
|
||||
ASSERT(pmtSource->pbFormat != NULL);
|
||||
pmtTarget->pbFormat = (PBYTE)CoTaskMemAlloc(pmtSource->cbFormat);
|
||||
if (pmtTarget->pbFormat == NULL) {
|
||||
pmtTarget->cbFormat = 0;
|
||||
return E_OUTOFMEMORY;
|
||||
} else {
|
||||
CopyMemory((PVOID)pmtTarget->pbFormat, (PVOID)pmtSource->pbFormat,
|
||||
pmtTarget->cbFormat);
|
||||
}
|
||||
}
|
||||
if (pmtTarget->pUnk != NULL) {
|
||||
pmtTarget->pUnk->AddRef();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Free an existing media type (ie free resources it holds)
|
||||
|
||||
void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt)
|
||||
{
|
||||
if (mt.cbFormat != 0) {
|
||||
CoTaskMemFree((PVOID)mt.pbFormat);
|
||||
|
||||
// Strictly unnecessary but tidier
|
||||
mt.cbFormat = 0;
|
||||
mt.pbFormat = NULL;
|
||||
}
|
||||
if (mt.pUnk != NULL) {
|
||||
mt.pUnk->Release();
|
||||
mt.pUnk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize a media type from a WAVEFORMATEX
|
||||
|
||||
STDAPI CreateAudioMediaType(
|
||||
const WAVEFORMATEX *pwfx,
|
||||
__out AM_MEDIA_TYPE *pmt,
|
||||
BOOL bSetFormat
|
||||
)
|
||||
{
|
||||
pmt->majortype = MEDIATYPE_Audio;
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
pmt->subtype = ((PWAVEFORMATEXTENSIBLE)pwfx)->SubFormat;
|
||||
} else {
|
||||
pmt->subtype = FOURCCMap(pwfx->wFormatTag);
|
||||
}
|
||||
pmt->formattype = FORMAT_WaveFormatEx;
|
||||
pmt->bFixedSizeSamples = TRUE;
|
||||
pmt->bTemporalCompression = FALSE;
|
||||
pmt->lSampleSize = pwfx->nBlockAlign;
|
||||
pmt->pUnk = NULL;
|
||||
if (bSetFormat) {
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {
|
||||
pmt->cbFormat = sizeof(WAVEFORMATEX);
|
||||
} else {
|
||||
pmt->cbFormat = sizeof(WAVEFORMATEX) + pwfx->cbSize;
|
||||
}
|
||||
pmt->pbFormat = (PBYTE)CoTaskMemAlloc(pmt->cbFormat);
|
||||
if (pmt->pbFormat == NULL) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {
|
||||
CopyMemory(pmt->pbFormat, pwfx, sizeof(PCMWAVEFORMAT));
|
||||
((WAVEFORMATEX *)pmt->pbFormat)->cbSize = 0;
|
||||
} else {
|
||||
CopyMemory(pmt->pbFormat, pwfx, pmt->cbFormat);
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// eliminate very many spurious warnings from MS compiler
|
||||
#pragma warning(disable:4514)
|
||||
|
||||
#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */
|
||||
@@ -0,0 +1,89 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: MtType.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a class that holds and manages
|
||||
// media type information.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __MTYPE__
|
||||
#define __MTYPE__
|
||||
|
||||
/* Helper class that derived pin objects can use to compare media
|
||||
types etc. Has same data members as the struct AM_MEDIA_TYPE defined
|
||||
in the streams IDL file, but also has (non-virtual) functions */
|
||||
|
||||
class CMediaType : public _AMMediaType {
|
||||
|
||||
public:
|
||||
|
||||
~CMediaType();
|
||||
CMediaType();
|
||||
CMediaType(const GUID * majortype);
|
||||
CMediaType(const AM_MEDIA_TYPE&, __out_opt HRESULT* phr = NULL);
|
||||
CMediaType(const CMediaType&, __out_opt HRESULT* phr = NULL);
|
||||
|
||||
CMediaType& operator=(const CMediaType&);
|
||||
CMediaType& operator=(const AM_MEDIA_TYPE&);
|
||||
|
||||
BOOL operator == (const CMediaType&) const;
|
||||
BOOL operator != (const CMediaType&) const;
|
||||
|
||||
HRESULT Set(const CMediaType& rt);
|
||||
HRESULT Set(const AM_MEDIA_TYPE& rt);
|
||||
|
||||
BOOL IsValid() const;
|
||||
|
||||
const GUID *Type() const { return &majortype;} ;
|
||||
void SetType(const GUID *);
|
||||
const GUID *Subtype() const { return &subtype;} ;
|
||||
void SetSubtype(const GUID *);
|
||||
|
||||
BOOL IsFixedSize() const {return bFixedSizeSamples; };
|
||||
BOOL IsTemporalCompressed() const {return bTemporalCompression; };
|
||||
ULONG GetSampleSize() const;
|
||||
|
||||
void SetSampleSize(ULONG sz);
|
||||
void SetVariableSize();
|
||||
void SetTemporalCompression(BOOL bCompressed);
|
||||
|
||||
// read/write pointer to format - can't change length without
|
||||
// calling SetFormat, AllocFormatBuffer or ReallocFormatBuffer
|
||||
|
||||
BYTE* Format() const {return pbFormat; };
|
||||
ULONG FormatLength() const { return cbFormat; };
|
||||
|
||||
void SetFormatType(const GUID *);
|
||||
const GUID *FormatType() const {return &formattype; };
|
||||
BOOL SetFormat(__in_bcount(length) BYTE *pFormat, ULONG length);
|
||||
void ResetFormatBuffer();
|
||||
BYTE* AllocFormatBuffer(ULONG length);
|
||||
BYTE* ReallocFormatBuffer(ULONG length);
|
||||
|
||||
void InitMediaType();
|
||||
|
||||
BOOL MatchesPartial(const CMediaType* ppartial) const;
|
||||
BOOL IsPartiallySpecified(void) const;
|
||||
};
|
||||
|
||||
|
||||
/* General purpose functions to copy and delete a task allocated AM_MEDIA_TYPE
|
||||
structure which is useful when using the IEnumMediaFormats interface as
|
||||
the implementation allocates the structures which you must later delete */
|
||||
|
||||
void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt);
|
||||
AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc);
|
||||
HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource);
|
||||
void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt);
|
||||
|
||||
// Initialize a media type from a WAVEFORMATEX
|
||||
|
||||
STDAPI CreateAudioMediaType(
|
||||
const WAVEFORMATEX *pwfx,
|
||||
__out AM_MEDIA_TYPE *pmt,
|
||||
BOOL bSetFormat);
|
||||
|
||||
#endif /* __MTYPE__ */
|
||||
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: RefClock.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines the IReferenceClock interface.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __BASEREFCLOCK__
|
||||
#define __BASEREFCLOCK__
|
||||
|
||||
#include <Schedule.h>
|
||||
|
||||
const UINT RESOLUTION = 1; /* High resolution timer */
|
||||
const INT ADVISE_CACHE = 4; /* Default cache size */
|
||||
const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF; /* Maximum LONGLONG value */
|
||||
|
||||
inline LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME& RT)
|
||||
{
|
||||
/* This converts an arbitrary value representing a reference time
|
||||
into a MILLISECONDS value for use in subsequent system calls */
|
||||
|
||||
return (RT / (UNITS / MILLISECONDS));
|
||||
}
|
||||
|
||||
/* This class hierarchy will support an IReferenceClock interface so
|
||||
that an audio card (or other externally driven clock) can update the
|
||||
system wide clock that everyone uses.
|
||||
|
||||
The interface will be pretty thin with probably just one update method
|
||||
This interface has not yet been defined.
|
||||
*/
|
||||
|
||||
/* This abstract base class implements the IReferenceClock
|
||||
* interface. Classes that actually provide clock signals (from
|
||||
* whatever source) have to be derived from this class.
|
||||
*
|
||||
* The abstract class provides implementations for:
|
||||
* CUnknown support
|
||||
* locking support (CCritSec)
|
||||
* client advise code (creates a thread)
|
||||
*
|
||||
* Question: what can we do about quality? Change the timer
|
||||
* resolution to lower the system load? Up the priority of the
|
||||
* timer thread to force more responsive signals?
|
||||
*
|
||||
* During class construction we create a worker thread that is destroyed during
|
||||
* destuction. This thread executes a series of WaitForSingleObject calls,
|
||||
* waking up when a command is given to the thread or the next wake up point
|
||||
* is reached. The wakeup points are determined by clients making Advise
|
||||
* calls.
|
||||
*
|
||||
* Each advise call defines a point in time when they wish to be notified. A
|
||||
* periodic advise is a series of these such events. We maintain a list of
|
||||
* advise links and calculate when the nearest event notification is due for.
|
||||
* We then call WaitForSingleObject with a timeout equal to this time. The
|
||||
* handle we wait on is used by the class to signal that something has changed
|
||||
* and that we must reschedule the next event. This typically happens when
|
||||
* someone comes in and asks for an advise link while we are waiting for an
|
||||
* event to timeout.
|
||||
*
|
||||
* While we are modifying the list of advise requests we
|
||||
* are protected from interference through a critical section. Clients are NOT
|
||||
* advised through callbacks. One shot clients have an event set, while
|
||||
* periodic clients have a semaphore released for each event notification. A
|
||||
* semaphore allows a client to be kept up to date with the number of events
|
||||
* actually triggered and be assured that they can't miss multiple events being
|
||||
* set.
|
||||
*
|
||||
* Keeping track of advises is taken care of by the CAMSchedule class.
|
||||
*/
|
||||
|
||||
class CBaseReferenceClock
|
||||
: public CUnknown, public IReferenceClock, public CCritSec, public IReferenceClockTimerControl
|
||||
{
|
||||
protected:
|
||||
virtual ~CBaseReferenceClock(); // Don't let me be created on the stack!
|
||||
public:
|
||||
CBaseReferenceClock(__in_opt LPCTSTR pName,
|
||||
__inout_opt LPUNKNOWN pUnk,
|
||||
__inout HRESULT *phr,
|
||||
__inout_opt CAMSchedule * pSched = 0 );
|
||||
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
|
||||
/* IReferenceClock methods */
|
||||
// Derived classes must implement GetPrivateTime(). All our GetTime
|
||||
// does is call GetPrivateTime and then check so that time does not
|
||||
// go backwards. A return code of S_FALSE implies that the internal
|
||||
// clock has gone backwards and GetTime time has halted until internal
|
||||
// time has caught up. (Don't know if this will be much use to folk,
|
||||
// but it seems odd not to use the return code for something useful.)
|
||||
STDMETHODIMP GetTime(__out REFERENCE_TIME *pTime);
|
||||
// When this is called, it sets m_rtLastGotTime to the time it returns.
|
||||
|
||||
/* Provide standard mechanisms for scheduling events */
|
||||
|
||||
/* Ask for an async notification that a time has elapsed */
|
||||
STDMETHODIMP AdviseTime(
|
||||
REFERENCE_TIME baseTime, // base reference time
|
||||
REFERENCE_TIME streamTime, // stream offset time
|
||||
HEVENT hEvent, // advise via this event
|
||||
__out DWORD_PTR *pdwAdviseCookie// where your cookie goes
|
||||
);
|
||||
|
||||
/* Ask for an asynchronous periodic notification that a time has elapsed */
|
||||
STDMETHODIMP AdvisePeriodic(
|
||||
REFERENCE_TIME StartTime, // starting at this time
|
||||
REFERENCE_TIME PeriodTime, // time between notifications
|
||||
HSEMAPHORE hSemaphore, // advise via a semaphore
|
||||
__out DWORD_PTR *pdwAdviseCookie// where your cookie goes
|
||||
);
|
||||
|
||||
/* Cancel a request for notification(s) - if the notification was
|
||||
* a one shot timer then this function doesn't need to be called
|
||||
* as the advise is automatically cancelled, however it does no
|
||||
* harm to explicitly cancel a one-shot advise. It is REQUIRED that
|
||||
* clients call Unadvise to clear a Periodic advise setting.
|
||||
*/
|
||||
|
||||
STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie);
|
||||
|
||||
/* Methods for the benefit of derived classes or outer objects */
|
||||
|
||||
// GetPrivateTime() is the REAL clock. GetTime is just a cover for
|
||||
// it. Derived classes will probably override this method but not
|
||||
// GetTime() itself.
|
||||
// The important point about GetPrivateTime() is it's allowed to go
|
||||
// backwards. Our GetTime() will keep returning the LastGotTime
|
||||
// until GetPrivateTime() catches up.
|
||||
virtual REFERENCE_TIME GetPrivateTime();
|
||||
|
||||
/* Provide a method for correcting drift */
|
||||
STDMETHODIMP SetTimeDelta( const REFERENCE_TIME& TimeDelta );
|
||||
|
||||
CAMSchedule * GetSchedule() const { return m_pSchedule; }
|
||||
|
||||
// IReferenceClockTimerControl methods
|
||||
//
|
||||
// Setting a default of 0 disables the default of 1ms
|
||||
STDMETHODIMP SetDefaultTimerResolution(
|
||||
REFERENCE_TIME timerResolution // in 100ns
|
||||
);
|
||||
STDMETHODIMP GetDefaultTimerResolution(
|
||||
__out REFERENCE_TIME* pTimerResolution // in 100ns
|
||||
);
|
||||
|
||||
private:
|
||||
REFERENCE_TIME m_rtPrivateTime; // Current best estimate of time
|
||||
DWORD m_dwPrevSystemTime; // Last vaule we got from timeGetTime
|
||||
REFERENCE_TIME m_rtLastGotTime; // Last time returned by GetTime
|
||||
REFERENCE_TIME m_rtNextAdvise; // Time of next advise
|
||||
UINT m_TimerResolution;
|
||||
|
||||
#ifdef PERF
|
||||
int m_idGetSystemTime;
|
||||
#endif
|
||||
|
||||
// Thread stuff
|
||||
public:
|
||||
void TriggerThread() // Wakes thread up. Need to do this if
|
||||
{ // time to next advise needs reevaluating.
|
||||
EXECUTE_ASSERT(SetEvent(m_pSchedule->GetEvent()));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
BOOL m_bAbort; // Flag used for thread shutdown
|
||||
HANDLE m_hThread; // Thread handle
|
||||
|
||||
HRESULT AdviseThread(); // Method in which the advise thread runs
|
||||
static DWORD __stdcall AdviseThreadFunction(__in LPVOID); // Function used to get there
|
||||
|
||||
protected:
|
||||
CAMSchedule * m_pSchedule;
|
||||
|
||||
void Restart (IN REFERENCE_TIME rtMinTime = 0I64) ;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: RefTime.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines CRefTime, a class that manages
|
||||
// reference times.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// CRefTime
|
||||
//
|
||||
// Manage reference times.
|
||||
// Shares same data layout as REFERENCE_TIME, but adds some (nonvirtual)
|
||||
// functions providing simple comparison, conversion and arithmetic.
|
||||
//
|
||||
// A reference time (at the moment) is a unit of seconds represented in
|
||||
// 100ns units as is used in the Win32 FILETIME structure. BUT the time
|
||||
// a REFERENCE_TIME represents is NOT the time elapsed since 1/1/1601 it
|
||||
// will either be stream time or reference time depending upon context
|
||||
//
|
||||
// This class provides simple arithmetic operations on reference times
|
||||
//
|
||||
// keep non-virtual otherwise the data layout will not be the same as
|
||||
// REFERENCE_TIME
|
||||
|
||||
|
||||
// -----
|
||||
// note that you are safe to cast a CRefTime* to a REFERENCE_TIME*, but
|
||||
// you will need to do so explicitly
|
||||
// -----
|
||||
|
||||
|
||||
#ifndef __REFTIME__
|
||||
#define __REFTIME__
|
||||
|
||||
|
||||
const LONGLONG MILLISECONDS = (1000); // 10 ^ 3
|
||||
const LONGLONG NANOSECONDS = (1000000000); // 10 ^ 9
|
||||
const LONGLONG UNITS = (NANOSECONDS / 100); // 10 ^ 7
|
||||
|
||||
/* Unfortunately an inline function here generates a call to __allmul
|
||||
- even for constants!
|
||||
*/
|
||||
#define MILLISECONDS_TO_100NS_UNITS(lMs) \
|
||||
Int32x32To64((lMs), (UNITS / MILLISECONDS))
|
||||
|
||||
class CRefTime
|
||||
{
|
||||
public:
|
||||
|
||||
// *MUST* be the only data member so that this class is exactly
|
||||
// equivalent to a REFERENCE_TIME.
|
||||
// Also, must be *no virtual functions*
|
||||
|
||||
REFERENCE_TIME m_time;
|
||||
|
||||
inline CRefTime()
|
||||
{
|
||||
// default to 0 time
|
||||
m_time = 0;
|
||||
};
|
||||
|
||||
inline CRefTime(LONG msecs)
|
||||
{
|
||||
m_time = MILLISECONDS_TO_100NS_UNITS(msecs);
|
||||
};
|
||||
|
||||
inline CRefTime(REFERENCE_TIME rt)
|
||||
{
|
||||
m_time = rt;
|
||||
};
|
||||
|
||||
inline operator REFERENCE_TIME() const
|
||||
{
|
||||
return m_time;
|
||||
};
|
||||
|
||||
inline CRefTime& operator=(const CRefTime& rt)
|
||||
{
|
||||
m_time = rt.m_time;
|
||||
return *this;
|
||||
};
|
||||
|
||||
inline CRefTime& operator=(const LONGLONG ll)
|
||||
{
|
||||
m_time = ll;
|
||||
return *this;
|
||||
};
|
||||
|
||||
inline CRefTime& operator+=(const CRefTime& rt)
|
||||
{
|
||||
return (*this = *this + rt);
|
||||
};
|
||||
|
||||
inline CRefTime& operator-=(const CRefTime& rt)
|
||||
{
|
||||
return (*this = *this - rt);
|
||||
};
|
||||
|
||||
inline LONG Millisecs(void)
|
||||
{
|
||||
return (LONG)(m_time / (UNITS / MILLISECONDS));
|
||||
};
|
||||
|
||||
inline LONGLONG GetUnits(void)
|
||||
{
|
||||
return m_time;
|
||||
};
|
||||
};
|
||||
|
||||
const LONGLONG TimeZero = 0;
|
||||
|
||||
#endif /* __REFTIME__ */
|
||||
|
||||
+2862
File diff suppressed because it is too large.
Load diff
+478
@@ -0,0 +1,478 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: RenBase.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a generic ActiveX base renderer
|
||||
// class.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __RENBASE__
|
||||
#define __RENBASE__
|
||||
|
||||
// Forward class declarations
|
||||
|
||||
class CBaseRenderer;
|
||||
class CBaseVideoRenderer;
|
||||
class CRendererInputPin;
|
||||
|
||||
// This is our input pin class that channels calls to the renderer
|
||||
|
||||
class CRendererInputPin : public CBaseInputPin
|
||||
{
|
||||
protected:
|
||||
|
||||
CBaseRenderer *m_pRenderer;
|
||||
|
||||
public:
|
||||
|
||||
CRendererInputPin(__inout CBaseRenderer *pRenderer,
|
||||
__inout HRESULT *phr,
|
||||
__in_opt LPCWSTR Name);
|
||||
|
||||
// Overriden from the base pin classes
|
||||
|
||||
HRESULT BreakConnect();
|
||||
HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
HRESULT SetMediaType(const CMediaType *pmt);
|
||||
HRESULT CheckMediaType(const CMediaType *pmt);
|
||||
HRESULT Active();
|
||||
HRESULT Inactive();
|
||||
|
||||
// Add rendering behaviour to interface functions
|
||||
|
||||
STDMETHODIMP QueryId(__deref_out LPWSTR *Id);
|
||||
STDMETHODIMP EndOfStream();
|
||||
STDMETHODIMP BeginFlush();
|
||||
STDMETHODIMP EndFlush();
|
||||
STDMETHODIMP Receive(IMediaSample *pMediaSample);
|
||||
|
||||
// Helper
|
||||
IMemAllocator inline *Allocator() const
|
||||
{
|
||||
return m_pAllocator;
|
||||
}
|
||||
};
|
||||
|
||||
// Main renderer class that handles synchronisation and state changes
|
||||
|
||||
class CBaseRenderer : public CBaseFilter
|
||||
{
|
||||
protected:
|
||||
|
||||
friend class CRendererInputPin;
|
||||
|
||||
friend void CALLBACK EndOfStreamTimer(UINT uID, // Timer identifier
|
||||
UINT uMsg, // Not currently used
|
||||
DWORD_PTR dwUser, // User information
|
||||
DWORD_PTR dw1, // Windows reserved
|
||||
DWORD_PTR dw2); // Is also reserved
|
||||
|
||||
CRendererPosPassThru *m_pPosition; // Media seeking pass by object
|
||||
CAMEvent m_RenderEvent; // Used to signal timer events
|
||||
CAMEvent m_ThreadSignal; // Signalled to release worker thread
|
||||
CAMEvent m_evComplete; // Signalled when state complete
|
||||
BOOL m_bAbort; // Stop us from rendering more data
|
||||
BOOL m_bStreaming; // Are we currently streaming
|
||||
DWORD_PTR m_dwAdvise; // Timer advise cookie
|
||||
IMediaSample *m_pMediaSample; // Current image media sample
|
||||
BOOL m_bEOS; // Any more samples in the stream
|
||||
BOOL m_bEOSDelivered; // Have we delivered an EC_COMPLETE
|
||||
CRendererInputPin *m_pInputPin; // Our renderer input pin object
|
||||
CCritSec m_InterfaceLock; // Critical section for interfaces
|
||||
CCritSec m_RendererLock; // Controls access to internals
|
||||
IQualityControl * m_pQSink; // QualityControl sink
|
||||
BOOL m_bRepaintStatus; // Can we signal an EC_REPAINT
|
||||
// Avoid some deadlocks by tracking filter during stop
|
||||
volatile BOOL m_bInReceive; // Inside Receive between PrepareReceive
|
||||
// And actually processing the sample
|
||||
REFERENCE_TIME m_SignalTime; // Time when we signal EC_COMPLETE
|
||||
UINT m_EndOfStreamTimer; // Used to signal end of stream
|
||||
CCritSec m_ObjectCreationLock; // This lock protects the creation and
|
||||
// of m_pPosition and m_pInputPin. It
|
||||
// ensures that two threads cannot create
|
||||
// either object simultaneously.
|
||||
|
||||
public:
|
||||
|
||||
CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer
|
||||
__in_opt LPCTSTR pName, // Debug ONLY description
|
||||
__inout_opt LPUNKNOWN pUnk, // Aggregated owner object
|
||||
__inout HRESULT *phr); // General OLE return code
|
||||
|
||||
~CBaseRenderer();
|
||||
|
||||
// Overriden to say what interfaces we support and where
|
||||
|
||||
virtual HRESULT GetMediaPositionInterface(REFIID riid, __deref_out void **ppv);
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **);
|
||||
|
||||
virtual HRESULT SourceThreadCanWait(BOOL bCanWait);
|
||||
|
||||
#ifdef DEBUG
|
||||
// Debug only dump of the renderer state
|
||||
void DisplayRendererState();
|
||||
#endif
|
||||
virtual HRESULT WaitForRenderTime();
|
||||
virtual HRESULT CompleteStateChange(FILTER_STATE OldState);
|
||||
|
||||
// Return internal information about this filter
|
||||
|
||||
BOOL IsEndOfStream() { return m_bEOS; };
|
||||
BOOL IsEndOfStreamDelivered() { return m_bEOSDelivered; };
|
||||
BOOL IsStreaming() { return m_bStreaming; };
|
||||
void SetAbortSignal(BOOL bAbort) { m_bAbort = bAbort; };
|
||||
virtual void OnReceiveFirstSample(IMediaSample *pMediaSample) { };
|
||||
CAMEvent *GetRenderEvent() { return &m_RenderEvent; };
|
||||
|
||||
// Permit access to the transition state
|
||||
|
||||
void Ready() { m_evComplete.Set(); };
|
||||
void NotReady() { m_evComplete.Reset(); };
|
||||
BOOL CheckReady() { return m_evComplete.Check(); };
|
||||
|
||||
virtual int GetPinCount();
|
||||
virtual CBasePin *GetPin(int n);
|
||||
FILTER_STATE GetRealState();
|
||||
void SendRepaint();
|
||||
void SendNotifyWindow(IPin *pPin,HWND hwnd);
|
||||
BOOL OnDisplayChange();
|
||||
void SetRepaintStatus(BOOL bRepaint);
|
||||
|
||||
// Override the filter and pin interface functions
|
||||
|
||||
STDMETHODIMP Stop();
|
||||
STDMETHODIMP Pause();
|
||||
STDMETHODIMP Run(REFERENCE_TIME StartTime);
|
||||
STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State);
|
||||
STDMETHODIMP FindPin(LPCWSTR Id, __deref_out IPin **ppPin);
|
||||
|
||||
// These are available for a quality management implementation
|
||||
|
||||
virtual void OnRenderStart(IMediaSample *pMediaSample);
|
||||
virtual void OnRenderEnd(IMediaSample *pMediaSample);
|
||||
virtual HRESULT OnStartStreaming() { return NOERROR; };
|
||||
virtual HRESULT OnStopStreaming() { return NOERROR; };
|
||||
virtual void OnWaitStart() { };
|
||||
virtual void OnWaitEnd() { };
|
||||
virtual void PrepareRender() { };
|
||||
|
||||
#ifdef PERF
|
||||
REFERENCE_TIME m_trRenderStart; // Just before we started drawing
|
||||
// Set in OnRenderStart, Used in OnRenderEnd
|
||||
int m_idBaseStamp; // MSR_id for frame time stamp
|
||||
int m_idBaseRenderTime; // MSR_id for true wait time
|
||||
int m_idBaseAccuracy; // MSR_id for time frame is late (int)
|
||||
#endif
|
||||
|
||||
// Quality management implementation for scheduling rendering
|
||||
|
||||
virtual BOOL ScheduleSample(IMediaSample *pMediaSample);
|
||||
virtual HRESULT GetSampleTimes(IMediaSample *pMediaSample,
|
||||
__out REFERENCE_TIME *pStartTime,
|
||||
__out REFERENCE_TIME *pEndTime);
|
||||
|
||||
virtual HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,
|
||||
__out REFERENCE_TIME *ptrStart,
|
||||
__out REFERENCE_TIME *ptrEnd);
|
||||
|
||||
// Lots of end of stream complexities
|
||||
|
||||
void TimerCallback();
|
||||
void ResetEndOfStreamTimer();
|
||||
HRESULT NotifyEndOfStream();
|
||||
virtual HRESULT SendEndOfStream();
|
||||
virtual HRESULT ResetEndOfStream();
|
||||
virtual HRESULT EndOfStream();
|
||||
|
||||
// Rendering is based around the clock
|
||||
|
||||
void SignalTimerFired();
|
||||
virtual HRESULT CancelNotification();
|
||||
virtual HRESULT ClearPendingSample();
|
||||
|
||||
// Called when the filter changes state
|
||||
|
||||
virtual HRESULT Active();
|
||||
virtual HRESULT Inactive();
|
||||
virtual HRESULT StartStreaming();
|
||||
virtual HRESULT StopStreaming();
|
||||
virtual HRESULT BeginFlush();
|
||||
virtual HRESULT EndFlush();
|
||||
|
||||
// Deal with connections and type changes
|
||||
|
||||
virtual HRESULT BreakConnect();
|
||||
virtual HRESULT SetMediaType(const CMediaType *pmt);
|
||||
virtual HRESULT CompleteConnect(IPin *pReceivePin);
|
||||
|
||||
// These look after the handling of data samples
|
||||
|
||||
virtual HRESULT PrepareReceive(IMediaSample *pMediaSample);
|
||||
virtual HRESULT Receive(IMediaSample *pMediaSample);
|
||||
virtual BOOL HaveCurrentSample();
|
||||
virtual IMediaSample *GetCurrentSample();
|
||||
virtual HRESULT Render(IMediaSample *pMediaSample);
|
||||
|
||||
// Derived classes MUST override these
|
||||
virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE;
|
||||
virtual HRESULT CheckMediaType(const CMediaType *) PURE;
|
||||
|
||||
// Helper
|
||||
void WaitForReceiveToComplete();
|
||||
};
|
||||
|
||||
|
||||
// CBaseVideoRenderer is a renderer class (see its ancestor class) and
|
||||
// it handles scheduling of media samples so that they are drawn at the
|
||||
// correct time by the reference clock. It implements a degradation
|
||||
// strategy. Possible degradation modes are:
|
||||
// Drop frames here (only useful if the drawing takes significant time)
|
||||
// Signal supplier (upstream) to drop some frame(s) - i.e. one-off skip.
|
||||
// Signal supplier to change the frame rate - i.e. ongoing skipping.
|
||||
// Or any combination of the above.
|
||||
// In order to determine what's useful to try we need to know what's going
|
||||
// on. This is done by timing various operations (including the supplier).
|
||||
// This timing is done by using timeGetTime as it is accurate enough and
|
||||
// usually cheaper than calling the reference clock. It also tells the
|
||||
// truth if there is an audio break and the reference clock stops.
|
||||
// We provide a number of public entry points (named OnXxxStart, OnXxxEnd)
|
||||
// which the rest of the renderer calls at significant moments. These do
|
||||
// the timing.
|
||||
|
||||
// the number of frames that the sliding averages are averaged over.
|
||||
// the rule is (1024*NewObservation + (AVGPERIOD-1) * PreviousAverage)/AVGPERIOD
|
||||
#define AVGPERIOD 4
|
||||
#define DO_MOVING_AVG(avg,obs) (avg = (1024*obs + (AVGPERIOD-1)*avg)/AVGPERIOD)
|
||||
// Spot the bug in this macro - I can't. but it doesn't work!
|
||||
|
||||
class CBaseVideoRenderer : public CBaseRenderer, // Base renderer class
|
||||
public IQualProp, // Property page guff
|
||||
public IQualityControl // Allow throttling
|
||||
{
|
||||
protected:
|
||||
|
||||
// Hungarian:
|
||||
// tFoo is the time Foo in mSec (beware m_tStart from filter.h)
|
||||
// trBar is the time Bar by the reference clock
|
||||
|
||||
//******************************************************************
|
||||
// State variables to control synchronisation
|
||||
//******************************************************************
|
||||
|
||||
// Control of sending Quality messages. We need to know whether
|
||||
// we are in trouble (e.g. frames being dropped) and where the time
|
||||
// is being spent.
|
||||
|
||||
// When we drop a frame we play the next one early.
|
||||
// The frame after that is likely to wait before drawing and counting this
|
||||
// wait as spare time is unfair, so we count it as a zero wait.
|
||||
// We therefore need to know whether we are playing frames early or not.
|
||||
|
||||
int m_nNormal; // The number of consecutive frames
|
||||
// drawn at their normal time (not early)
|
||||
// -1 means we just dropped a frame.
|
||||
|
||||
#ifdef PERF
|
||||
BOOL m_bDrawLateFrames; // Don't drop any frames (debug and I'm
|
||||
// not keen on people using it!)
|
||||
#endif
|
||||
|
||||
BOOL m_bSupplierHandlingQuality;// The response to Quality messages says
|
||||
// our supplier is handling things.
|
||||
// We will allow things to go extra late
|
||||
// before dropping frames. We will play
|
||||
// very early after he has dropped one.
|
||||
|
||||
// Control of scheduling, frame dropping etc.
|
||||
// We need to know where the time is being spent so as to tell whether
|
||||
// we should be taking action here, signalling supplier or what.
|
||||
// The variables are initialised to a mode of NOT dropping frames.
|
||||
// They will tell the truth after a few frames.
|
||||
// We typically record a start time for an event, later we get the time
|
||||
// again and subtract to get the elapsed time, and we average this over
|
||||
// a few frames. The average is used to tell what mode we are in.
|
||||
|
||||
// Although these are reference times (64 bit) they are all DIFFERENCES
|
||||
// between times which are small. An int will go up to 214 secs before
|
||||
// overflow. Avoiding 64 bit multiplications and divisions seems
|
||||
// worth while.
|
||||
|
||||
|
||||
|
||||
// Audio-video throttling. If the user has turned up audio quality
|
||||
// very high (in principle it could be any other stream, not just audio)
|
||||
// then we can receive cries for help via the graph manager. In this case
|
||||
// we put in a wait for some time after rendering each frame.
|
||||
int m_trThrottle;
|
||||
|
||||
// The time taken to render (i.e. BitBlt) frames controls which component
|
||||
// needs to degrade. If the blt is expensive, the renderer degrades.
|
||||
// If the blt is cheap it's done anyway and the supplier degrades.
|
||||
int m_trRenderAvg; // Time frames are taking to blt
|
||||
int m_trRenderLast; // Time for last frame blt
|
||||
int m_tRenderStart; // Just before we started drawing (mSec)
|
||||
// derived from timeGetTime.
|
||||
|
||||
// When frames are dropped we will play the next frame as early as we can.
|
||||
// If it was a false alarm and the machine is fast we slide gently back to
|
||||
// normal timing. To do this, we record the offset showing just how early
|
||||
// we really are. This will normally be negative meaning early or zero.
|
||||
int m_trEarliness;
|
||||
|
||||
// Target provides slow long-term feedback to try to reduce the
|
||||
// average sync offset to zero. Whenever a frame is actually rendered
|
||||
// early we add a msec or two, whenever late we take off a few.
|
||||
// We add or take off 1/32 of the error time.
|
||||
// Eventually we should be hovering around zero. For a really bad case
|
||||
// where we were (say) 300mSec off, it might take 100 odd frames to
|
||||
// settle down. The rate of change of this is intended to be slower
|
||||
// than any other mechanism in Quartz, thereby avoiding hunting.
|
||||
int m_trTarget;
|
||||
|
||||
// The proportion of time spent waiting for the right moment to blt
|
||||
// controls whether we bother to drop a frame or whether we reckon that
|
||||
// we're doing well enough that we can stand a one-frame glitch.
|
||||
int m_trWaitAvg; // Average of last few wait times
|
||||
// (actually we just average how early
|
||||
// we were). Negative here means LATE.
|
||||
|
||||
// The average inter-frame time.
|
||||
// This is used to calculate the proportion of the time used by the
|
||||
// three operations (supplying us, waiting, rendering)
|
||||
int m_trFrameAvg; // Average inter-frame time
|
||||
int m_trDuration; // duration of last frame.
|
||||
|
||||
#ifdef PERF
|
||||
// Performance logging identifiers
|
||||
int m_idTimeStamp; // MSR_id for frame time stamp
|
||||
int m_idEarliness; // MSR_id for earliness fudge
|
||||
int m_idTarget; // MSR_id for Target fudge
|
||||
int m_idWaitReal; // MSR_id for true wait time
|
||||
int m_idWait; // MSR_id for wait time recorded
|
||||
int m_idFrameAccuracy; // MSR_id for time frame is late (int)
|
||||
int m_idRenderAvg; // MSR_id for Render time recorded (int)
|
||||
int m_idSchLateTime; // MSR_id for lateness at scheduler
|
||||
int m_idQualityRate; // MSR_id for Quality rate requested
|
||||
int m_idQualityTime; // MSR_id for Quality time requested
|
||||
int m_idDecision; // MSR_id for decision code
|
||||
int m_idDuration; // MSR_id for duration of a frame
|
||||
int m_idThrottle; // MSR_id for audio-video throttling
|
||||
//int m_idDebug; // MSR_id for trace style debugging
|
||||
//int m_idSendQuality; // MSR_id for timing the notifications per se
|
||||
#endif // PERF
|
||||
REFERENCE_TIME m_trRememberStampForPerf; // original time stamp of frame
|
||||
// with no earliness fudges etc.
|
||||
#ifdef PERF
|
||||
REFERENCE_TIME m_trRememberFrameForPerf; // time when previous frame rendered
|
||||
|
||||
// debug...
|
||||
int m_idFrameAvg;
|
||||
int m_idWaitAvg;
|
||||
#endif
|
||||
|
||||
// PROPERTY PAGE
|
||||
// This has edit fields that show the user what's happening
|
||||
// These member variables hold these counts.
|
||||
|
||||
int m_cFramesDropped; // cumulative frames dropped IN THE RENDERER
|
||||
int m_cFramesDrawn; // Frames since streaming started seen BY THE
|
||||
// RENDERER (some may be dropped upstream)
|
||||
|
||||
// Next two support average sync offset and standard deviation of sync offset.
|
||||
LONGLONG m_iTotAcc; // Sum of accuracies in mSec
|
||||
LONGLONG m_iSumSqAcc; // Sum of squares of (accuracies in mSec)
|
||||
|
||||
// Next two allow jitter calculation. Jitter is std deviation of frame time.
|
||||
REFERENCE_TIME m_trLastDraw; // Time of prev frame (for inter-frame times)
|
||||
LONGLONG m_iSumSqFrameTime; // Sum of squares of (inter-frame time in mSec)
|
||||
LONGLONG m_iSumFrameTime; // Sum of inter-frame times in mSec
|
||||
|
||||
// To get performance statistics on frame rate, jitter etc, we need
|
||||
// to record the lateness and inter-frame time. What we actually need are the
|
||||
// data above (sum, sum of squares and number of entries for each) but the data
|
||||
// is generated just ahead of time and only later do we discover whether the
|
||||
// frame was actually drawn or not. So we have to hang on to the data
|
||||
int m_trLate; // hold onto frame lateness
|
||||
int m_trFrame; // hold onto inter-frame time
|
||||
|
||||
int m_tStreamingStart; // if streaming then time streaming started
|
||||
// else time of last streaming session
|
||||
// used for property page statistics
|
||||
#ifdef PERF
|
||||
LONGLONG m_llTimeOffset; // timeGetTime()*10000+m_llTimeOffset==ref time
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
|
||||
CBaseVideoRenderer(REFCLSID RenderClass, // CLSID for this renderer
|
||||
__in_opt LPCTSTR pName, // Debug ONLY description
|
||||
__inout_opt LPUNKNOWN pUnk, // Aggregated owner object
|
||||
__inout HRESULT *phr); // General OLE return code
|
||||
|
||||
~CBaseVideoRenderer();
|
||||
|
||||
// IQualityControl methods - Notify allows audio-video throttling
|
||||
|
||||
STDMETHODIMP SetSink( IQualityControl * piqc);
|
||||
STDMETHODIMP Notify( IBaseFilter * pSelf, Quality q);
|
||||
|
||||
// These provide a full video quality management implementation
|
||||
|
||||
void OnRenderStart(IMediaSample *pMediaSample);
|
||||
void OnRenderEnd(IMediaSample *pMediaSample);
|
||||
void OnWaitStart();
|
||||
void OnWaitEnd();
|
||||
HRESULT OnStartStreaming();
|
||||
HRESULT OnStopStreaming();
|
||||
void ThrottleWait();
|
||||
|
||||
// Handle the statistics gathering for our quality management
|
||||
|
||||
void PreparePerformanceData(int trLate, int trFrame);
|
||||
virtual void RecordFrameLateness(int trLate, int trFrame);
|
||||
virtual void OnDirectRender(IMediaSample *pMediaSample);
|
||||
virtual HRESULT ResetStreamingTimes();
|
||||
BOOL ScheduleSample(IMediaSample *pMediaSample);
|
||||
HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,
|
||||
__inout REFERENCE_TIME *ptrStart,
|
||||
__inout REFERENCE_TIME *ptrEnd);
|
||||
|
||||
virtual HRESULT SendQuality(REFERENCE_TIME trLate, REFERENCE_TIME trRealStream);
|
||||
STDMETHODIMP JoinFilterGraph(__inout_opt IFilterGraph * pGraph, __in_opt LPCWSTR pName);
|
||||
|
||||
//
|
||||
// Do estimates for standard deviations for per-frame
|
||||
// statistics
|
||||
//
|
||||
// *piResult = (llSumSq - iTot * iTot / m_cFramesDrawn - 1) /
|
||||
// (m_cFramesDrawn - 2)
|
||||
// or 0 if m_cFramesDrawn <= 3
|
||||
//
|
||||
HRESULT GetStdDev(
|
||||
int nSamples,
|
||||
__out int *piResult,
|
||||
LONGLONG llSumSq,
|
||||
LONGLONG iTot
|
||||
);
|
||||
public:
|
||||
|
||||
// IQualProp property page support
|
||||
|
||||
STDMETHODIMP get_FramesDroppedInRenderer(__out int *cFramesDropped);
|
||||
STDMETHODIMP get_FramesDrawn(__out int *pcFramesDrawn);
|
||||
STDMETHODIMP get_AvgFrameRate(__out int *piAvgFrameRate);
|
||||
STDMETHODIMP get_Jitter(__out int *piJitter);
|
||||
STDMETHODIMP get_AvgSyncOffset(__out int *piAvg);
|
||||
STDMETHODIMP get_DevSyncOffset(__out int *piDev);
|
||||
|
||||
// Implement an IUnknown interface and expose IQualProp
|
||||
|
||||
DECLARE_IUNKNOWN
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out VOID **ppv);
|
||||
};
|
||||
|
||||
#endif // __RENBASE__
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
See https://github.com/RobertBeckebans/RBDOOM-3-BFG/blob/master/neo/libs/mingw-hacks/sal.h
|
||||
*/
|
||||
/* From PortAudio, which is under MIT license:
|
||||
* https://www.assembla.com/code/portaudio/subversion/nodes/portaudio/trunk/src/hostapi/wasapi/mingw-include/sal.h
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if __GNUC__ >=3
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#define __in
|
||||
#define __out
|
||||
#define __deref_in
|
||||
#define __deref_inout_opt
|
||||
#define __field_ecount_opt(x)
|
||||
#define __in_bcount_opt(size)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: SeekPT.h
|
||||
//
|
||||
// Desc: DirectShow base classes.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __seekpt_h__
|
||||
#define __seekpt_h__
|
||||
|
||||
|
||||
class CSeekingPassThru : public ISeekingPassThru, public CUnknown
|
||||
{
|
||||
public:
|
||||
static CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);
|
||||
CSeekingPassThru(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);
|
||||
~CSeekingPassThru();
|
||||
|
||||
DECLARE_IUNKNOWN;
|
||||
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
|
||||
|
||||
STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin);
|
||||
|
||||
private:
|
||||
CPosPassThru *m_pPosPassThru;
|
||||
};
|
||||
|
||||
#endif
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: Streams.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines overall streams architecture.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __STREAMS__
|
||||
#define __STREAMS__
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// disable some level-4 warnings, use #pragma warning(enable:###) to re-enable
|
||||
#pragma warning(disable:4100) // warning C4100: unreferenced formal parameter
|
||||
#pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
|
||||
#pragma warning(disable:4511) // warning C4511: copy constructor could not be generated
|
||||
#pragma warning(disable:4512) // warning C4512: assignment operator could not be generated
|
||||
#pragma warning(disable:4514) // warning C4514: "unreferenced inline function has been removed"
|
||||
|
||||
#if _MSC_VER>=1100
|
||||
#define AM_NOVTABLE __declspec(novtable)
|
||||
#else
|
||||
#define AM_NOVTABLE
|
||||
#endif
|
||||
#else
|
||||
#include <sal.h>
|
||||
#include <sal2.h>
|
||||
#define AM_NOVTABLE
|
||||
#pragma GCC diagnostic ignored "-Wwrite-strings"
|
||||
#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>
|
||||
#include <ddraw.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
|
||||
#ifndef NUMELMS
|
||||
#if _WIN32_WINNT < 0x0600
|
||||
#define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
|
||||
#else
|
||||
#define NUMELMS(aa) ARRAYSIZE(aa)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// The following definitions come from the Platform SDK and are required if
|
||||
// the applicaiton is being compiled with the headers from Visual C++ 6.0.
|
||||
/////////////////////////////////////////////////// ////////////////////////
|
||||
#ifndef InterlockedExchangePointer
|
||||
#define InterlockedExchangePointer(Target, Value) \
|
||||
(PVOID)InterlockedExchange((PLONG)(Target), (LONG)(Value))
|
||||
#endif
|
||||
|
||||
#ifndef _WAVEFORMATEXTENSIBLE_
|
||||
#define _WAVEFORMATEXTENSIBLE_
|
||||
typedef struct {
|
||||
WAVEFORMATEX Format;
|
||||
union {
|
||||
WORD wValidBitsPerSample; /* bits of precision */
|
||||
WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
|
||||
WORD wReserved; /* If neither applies, set to zero. */
|
||||
} Samples;
|
||||
DWORD dwChannelMask; /* which channels are */
|
||||
/* present in stream */
|
||||
GUID SubFormat;
|
||||
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
|
||||
#endif // !_WAVEFORMATEXTENSIBLE_
|
||||
|
||||
#if !defined(WAVE_FORMAT_EXTENSIBLE)
|
||||
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
|
||||
#endif // !defined(WAVE_FORMAT_EXTENSIBLE)
|
||||
|
||||
#ifndef GetWindowLongPtr
|
||||
#define GetWindowLongPtrA GetWindowLongA
|
||||
#define GetWindowLongPtrW GetWindowLongW
|
||||
#ifdef UNICODE
|
||||
#define GetWindowLongPtr GetWindowLongPtrW
|
||||
#else
|
||||
#define GetWindowLongPtr GetWindowLongPtrA
|
||||
#endif // !UNICODE
|
||||
#endif // !GetWindowLongPtr
|
||||
|
||||
#ifndef SetWindowLongPtr
|
||||
#define SetWindowLongPtrA SetWindowLongA
|
||||
#define SetWindowLongPtrW SetWindowLongW
|
||||
#ifdef UNICODE
|
||||
#define SetWindowLongPtr SetWindowLongPtrW
|
||||
#else
|
||||
#define SetWindowLongPtr SetWindowLongPtrA
|
||||
#endif // !UNICODE
|
||||
#endif // !SetWindowLongPtr
|
||||
|
||||
#ifndef GWLP_WNDPROC
|
||||
#define GWLP_WNDPROC (-4)
|
||||
#endif
|
||||
#ifndef GWLP_HINSTANCE
|
||||
#define GWLP_HINSTANCE (-6)
|
||||
#endif
|
||||
#ifndef GWLP_HWNDPARENT
|
||||
#define GWLP_HWNDPARENT (-8)
|
||||
#endif
|
||||
#ifndef GWLP_USERDATA
|
||||
#define GWLP_USERDATA (-21)
|
||||
#endif
|
||||
#ifndef GWLP_ID
|
||||
#define GWLP_ID (-12)
|
||||
#endif
|
||||
#ifndef DWLP_MSGRESULT
|
||||
#define DWLP_MSGRESULT 0
|
||||
#endif
|
||||
#ifndef DWLP_DLGPROC
|
||||
#define DWLP_DLGPROC DWLP_MSGRESULT + sizeof(LRESULT)
|
||||
#endif
|
||||
#ifndef DWLP_USER
|
||||
#define DWLP_USER DWLP_DLGPROC + sizeof(DLGPROC)
|
||||
#endif
|
||||
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4312 4244)
|
||||
// _GetWindowLongPtr
|
||||
// Templated version of GetWindowLongPtr, to suppress spurious compiler warning.
|
||||
template <class T>
|
||||
T _GetWindowLongPtr(HWND hwnd, int nIndex)
|
||||
{
|
||||
return (T)GetWindowLongPtr(hwnd, nIndex);
|
||||
}
|
||||
|
||||
// _SetWindowLongPtr
|
||||
// Templated version of SetWindowLongPtr, to suppress spurious compiler warning.
|
||||
template <class T>
|
||||
LONG_PTR _SetWindowLongPtr(HWND hwnd, int nIndex, T p)
|
||||
{
|
||||
return SetWindowLongPtr(hwnd, nIndex, (LONG_PTR)p);
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// End Platform SDK definitions
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include <strmif.h> // Generated IDL header file for streams interfaces
|
||||
#include <intsafe.h> // required by amvideo.h
|
||||
|
||||
#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 DX 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
|
||||
const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF; /* Maximum LONGLONG value */
|
||||
//#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
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
||||
#define min(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
|
||||
#include <amvideo2.h>
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#else
|
||||
#ifdef DEBUG
|
||||
#pragma message("STREAMS.H included TWICE")
|
||||
#endif
|
||||
#endif // __STREAMS__
|
||||
|
||||
+1478
File diff suppressed because it is too large.
Load diff
+359
@@ -0,0 +1,359 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: WXDebug.h
|
||||
//
|
||||
// Desc: DirectShow base classes - provides debugging facilities.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __WXDEBUG__
|
||||
#define __WXDEBUG__
|
||||
|
||||
// This library provides fairly straight forward debugging functionality, this
|
||||
// is split into two main sections. The first is assertion handling, there are
|
||||
// three types of assertions provided here. The most commonly used one is the
|
||||
// ASSERT(condition) macro which will pop up a message box including the file
|
||||
// and line number if the condition evaluates to FALSE. Then there is the
|
||||
// EXECUTE_ASSERT macro which is the same as ASSERT except the condition will
|
||||
// still be executed in NON debug builds. The final type of assertion is the
|
||||
// KASSERT macro which is more suitable for pure (perhaps kernel) filters as
|
||||
// the condition is printed onto the debugger rather than in a message box.
|
||||
//
|
||||
// The other part of the debug module facilties is general purpose logging.
|
||||
// This is accessed by calling DbgLog(). The function takes a type and level
|
||||
// field which define the type of informational string you are presenting and
|
||||
// it's relative importance. The type field can be a combination (one or more)
|
||||
// of LOG_TIMING, LOG_TRACE, LOG_MEMORY, LOG_LOCKING and LOG_ERROR. The level
|
||||
// is a DWORD value where zero defines highest important. Use of zero as the
|
||||
// debug logging level is to be encouraged ONLY for major errors or events as
|
||||
// they will ALWAYS be displayed on the debugger. Other debug output has it's
|
||||
// level matched against the current debug output level stored in the registry
|
||||
// for this module and if less than the current setting it will be displayed.
|
||||
//
|
||||
// Each module or executable has it's own debug output level for each of the
|
||||
// five types. These are read in when the DbgInitialise function is called
|
||||
// for DLLs linking to STRMBASE.LIB this is done automatically when the DLL
|
||||
// is loaded, executables must call it explicitely with the module instance
|
||||
// handle given to them through the WINMAIN entry point. An executable must
|
||||
// also call DbgTerminate when they have finished to clean up the resources
|
||||
// the debug library uses, once again this is done automatically for DLLs
|
||||
|
||||
// These are the five different categories of logging information
|
||||
|
||||
enum { LOG_TIMING = 0x01, // Timing and performance measurements
|
||||
LOG_TRACE = 0x02, // General step point call tracing
|
||||
LOG_MEMORY = 0x04, // Memory and object allocation/destruction
|
||||
LOG_LOCKING = 0x08, // Locking/unlocking of critical sections
|
||||
LOG_ERROR = 0x10, // Debug error notification
|
||||
LOG_CUSTOM1 = 0x20,
|
||||
LOG_CUSTOM2 = 0x40,
|
||||
LOG_CUSTOM3 = 0x80,
|
||||
LOG_CUSTOM4 = 0x100,
|
||||
LOG_CUSTOM5 = 0x200,
|
||||
};
|
||||
|
||||
#define LOG_FORCIBLY_SET 0x80000000
|
||||
|
||||
enum { CDISP_HEX = 0x01,
|
||||
CDISP_DEC = 0x02};
|
||||
|
||||
// For each object created derived from CBaseObject (in debug builds) we
|
||||
// create a descriptor that holds it's name (statically allocated memory)
|
||||
// and a cookie we assign it. We keep a list of all the active objects
|
||||
// we have registered so that we can dump a list of remaining objects
|
||||
|
||||
typedef struct tag_ObjectDesc {
|
||||
LPCSTR m_szName;
|
||||
LPCWSTR 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(
|
||||
LPCSTR szObjectName, LPCWSTR 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(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);
|
||||
void WINAPI DbgBreakPoint(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);
|
||||
void WINAPI DbgBreakPoint(LPCTSTR pFileName,INT iLine,__format_string LPCTSTR szFormatString,...);
|
||||
|
||||
void WINAPI DbgKernelAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);
|
||||
void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCTSTR pFormat,...);
|
||||
#ifdef UNICODE
|
||||
void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCSTR pFormat,...);
|
||||
void WINAPI DbgAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine);
|
||||
void WINAPI DbgBreakPoint(LPCSTR pCondition,LPCSTR pFileName,INT iLine);
|
||||
void WINAPI DbgKernelAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine);
|
||||
#endif
|
||||
void WINAPI DbgOutString(LPCTSTR psz);
|
||||
|
||||
// Debug infinite wait stuff
|
||||
DWORD WINAPI DbgWaitForSingleObject(HANDLE h);
|
||||
DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount,
|
||||
__in_ecount(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(LPCTSTR label, const AM_MEDIA_TYPE *pmtIn);
|
||||
|
||||
// Dump lots of information about a filter graph
|
||||
void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel);
|
||||
#endif
|
||||
|
||||
#define KASSERT(_x_) if (!(_x_)) \
|
||||
DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
|
||||
// Break on the debugger without putting up a message box
|
||||
// message goes to debugger instead
|
||||
|
||||
#define KDbgBreak(_x_) \
|
||||
DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
|
||||
// We chose a common name for our ASSERT macro, MFC also uses this name
|
||||
// So long as the implementation evaluates the condition and handles it
|
||||
// then we will be ok. Rather than override the behaviour expected we
|
||||
// will leave whatever first defines ASSERT as the handler (i.e. MFC)
|
||||
#ifndef ASSERT
|
||||
#define ASSERT(_x_) if (!(_x_)) \
|
||||
DbgAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
#endif
|
||||
|
||||
#define DbgAssertAligned( _ptr_, _alignment_ ) ASSERT( ((DWORD_PTR) (_ptr_)) % (_alignment_) == 0)
|
||||
|
||||
// Put up a message box informing the user of a halt
|
||||
// condition in the program
|
||||
|
||||
#define DbgBreak(_x_) \
|
||||
DbgBreakPoint(TEXT(#_x_),TEXT(__FILE__),__LINE__)
|
||||
|
||||
#define EXECUTE_ASSERT(_x_) ASSERT(_x_)
|
||||
#define DbgLog(_x_) DbgLogInfo _x_
|
||||
// MFC style trace macros
|
||||
|
||||
#define NOTE(_x_) DbgLog((LOG_TRACE,5,TEXT(_x_)))
|
||||
#define NOTE1(_x_,a) DbgLog((LOG_TRACE,5,TEXT(_x_),a))
|
||||
#define NOTE2(_x_,a,b) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b))
|
||||
#define NOTE3(_x_,a,b,c) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c))
|
||||
#define NOTE4(_x_,a,b,c,d) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d))
|
||||
#define NOTE5(_x_,a,b,c,d,e) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d,e))
|
||||
|
||||
#else
|
||||
|
||||
// Retail builds make public debug functions inert - WARNING the source
|
||||
// files do not define or build any of the entry points in debug builds
|
||||
// (public entry points compile to nothing) so if you go trying to call
|
||||
// any of the private entry points in your source they won't compile
|
||||
|
||||
#define NAME(_x_) ((LPTSTR) 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)
|
||||
// }
|
||||
|
||||
#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
|
||||
|
||||
|
||||
#ifdef _OBJBASE_H_
|
||||
|
||||
// Outputting GUID names. If you want to include the name
|
||||
// associated with a GUID (eg CLSID_...) then
|
||||
//
|
||||
// GuidNames[yourGUID]
|
||||
//
|
||||
// Returns the name defined in uuids.h as a string
|
||||
|
||||
typedef struct {
|
||||
CHAR *szName;
|
||||
GUID guid;
|
||||
} GUID_STRING_ENTRY;
|
||||
|
||||
class CGuidNameList {
|
||||
public:
|
||||
CHAR *operator [] (const GUID& guid);
|
||||
};
|
||||
|
||||
extern CGuidNameList GuidNames;
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef REMIND
|
||||
// REMIND macro - generates warning as reminder to complete coding
|
||||
// (eg) usage:
|
||||
//
|
||||
// #pragma message (REMIND("Add automation support"))
|
||||
|
||||
|
||||
#define QUOTE(x) #x
|
||||
#define QQUOTE(y) QUOTE(y)
|
||||
#define REMIND(str) __FILE__ "(" QQUOTE(__LINE__) ") : " str
|
||||
#endif
|
||||
|
||||
// Method to display objects in a useful format
|
||||
//
|
||||
// eg If you want to display a LONGLONG ll in a debug string do (eg)
|
||||
//
|
||||
// DbgLog((LOG_TRACE, n, TEXT("Value is %s"), (LPCTSTR)CDisp(ll, CDISP_HEX)));
|
||||
|
||||
|
||||
class CDispBasic
|
||||
{
|
||||
public:
|
||||
CDispBasic() { m_pString = m_String; };
|
||||
~CDispBasic();
|
||||
protected:
|
||||
PTCHAR m_pString; // normally points to m_String... unless too much data
|
||||
TCHAR m_String[50];
|
||||
};
|
||||
class CDisp : public CDispBasic
|
||||
{
|
||||
public:
|
||||
CDisp(LONGLONG ll, int Format = CDISP_HEX); // Display a LONGLONG in CDISP_HEX or CDISP_DEC form
|
||||
CDisp(REFCLSID clsid); // Display a GUID
|
||||
CDisp(double d); // Display a floating point number
|
||||
#ifdef __strmif_h__
|
||||
#ifdef __STREAMS__
|
||||
CDisp(CRefTime t); // Display a Reference Time
|
||||
#endif
|
||||
CDisp(IPin *pPin); // Display a pin as {filter clsid}(pin name)
|
||||
CDisp(IUnknown *pUnk); // Display a filter or pin
|
||||
#endif // __strmif_h__
|
||||
~CDisp();
|
||||
|
||||
// Implement cast to (LPCTSTR) as parameter to logger
|
||||
operator LPCTSTR()
|
||||
{
|
||||
return (LPCTSTR)m_pString;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#if defined(DEBUG)
|
||||
class CAutoTrace
|
||||
{
|
||||
private:
|
||||
LPCTSTR _szBlkName;
|
||||
const int _level;
|
||||
static const TCHAR _szEntering[];
|
||||
static const TCHAR _szLeaving[];
|
||||
public:
|
||||
CAutoTrace(LPCTSTR 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__
|
||||
|
||||
|
||||
+896
@@ -0,0 +1,896 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: WXList.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements a non-MFC based generic list
|
||||
// template class.
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <pjmedia-videodev/config.h>
|
||||
|
||||
#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
|
||||
|
||||
/* 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(__in_opt LPCTSTR 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(__in_opt LPCTSTR 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(__in_opt LPCSTR 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(__in_opt LPCSTR 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).
|
||||
*/
|
||||
__out_opt POSITION CBaseList::GetHeadPositionI() const
|
||||
{
|
||||
return (POSITION) m_pFirst;
|
||||
} // GetHeadPosition
|
||||
|
||||
|
||||
|
||||
__out_opt POSITION CBaseList::GetTailPositionI() const
|
||||
{
|
||||
return (POSITION) m_pLast;
|
||||
} // GetTailPosition
|
||||
|
||||
|
||||
|
||||
/* Get the number of objects in the list,
|
||||
Get the lock before accessing the count.
|
||||
Locking may not be entirely necessary but it has the side effect
|
||||
of making sure that all operations are complete before we get it.
|
||||
So for example if a list is being added to this list then that
|
||||
will have completed in full before we continue rather than seeing
|
||||
an intermediate albeit valid state
|
||||
*/
|
||||
int CBaseList::GetCountI() const
|
||||
{
|
||||
return m_Count;
|
||||
} // GetCount
|
||||
|
||||
|
||||
|
||||
/* Return the object at rp, update rp to the next object from
|
||||
the list or NULL if you have moved over the last object.
|
||||
You may still call this function once we return NULL but
|
||||
we will continue to return a NULL position value
|
||||
*/
|
||||
__out void *CBaseList::GetNextI(__inout POSITION& rp) const
|
||||
{
|
||||
/* have we reached the end of the list */
|
||||
|
||||
if (rp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lock the object before continuing */
|
||||
|
||||
void *pObject;
|
||||
|
||||
/* Copy the original position then step on */
|
||||
|
||||
CNode *pn = (CNode *) rp;
|
||||
ASSERT(pn != NULL);
|
||||
rp = (POSITION) pn->Next();
|
||||
|
||||
/* Get the object at the original position from the list */
|
||||
|
||||
pObject = pn->GetData();
|
||||
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
|
||||
return pObject;
|
||||
} //GetNext
|
||||
|
||||
|
||||
|
||||
/* Return the object at p.
|
||||
Asking for the object at NULL ASSERTs then returns NULL
|
||||
The object is NOT locked. The list is not being changed
|
||||
in any way. If another thread is busy deleting the object
|
||||
then locking would only result in a change from one bad
|
||||
behaviour to another.
|
||||
*/
|
||||
__out_opt void *CBaseList::GetI(__in_opt 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
|
||||
|
||||
__out void *CBaseList::GetValidI(__in POSITION p) const
|
||||
{
|
||||
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.
|
||||
*/
|
||||
__out_opt POSITION CBaseList::FindI( __in void * pObj) const
|
||||
{
|
||||
POSITION pn;
|
||||
INTERNALTRAVERSELIST(*this, pn){
|
||||
if (GetI(pn)==pObj) {
|
||||
return pn;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
} // Find
|
||||
|
||||
|
||||
|
||||
/* Remove the first node in the list (deletes the pointer to its object
|
||||
from the list, does not free the object itself).
|
||||
Return the pointer to its object or NULL if empty
|
||||
*/
|
||||
__out_opt void *CBaseList::RemoveHeadI()
|
||||
{
|
||||
/* All we do is get the head position and ask for that to be deleted.
|
||||
We could special case this since some of the code path checking
|
||||
in Remove() is redundant as we know there is no previous
|
||||
node for example but it seems to gain little over the
|
||||
added complexity
|
||||
*/
|
||||
|
||||
return RemoveI((POSITION)m_pFirst);
|
||||
} // RemoveHead
|
||||
|
||||
|
||||
|
||||
/* Remove the last node in the list (deletes the pointer to its object
|
||||
from the list, does not free the object itself).
|
||||
Return the pointer to its object or NULL if empty
|
||||
*/
|
||||
__out_opt void *CBaseList::RemoveTailI()
|
||||
{
|
||||
/* All we do is get the tail position and ask for that to be deleted.
|
||||
We could special case this since some of the code path checking
|
||||
in Remove() is redundant as we know there is no previous
|
||||
node for example but it seems to gain little over the
|
||||
added complexity
|
||||
*/
|
||||
|
||||
return RemoveI((POSITION)m_pLast);
|
||||
} // RemoveTail
|
||||
|
||||
|
||||
|
||||
/* Remove the pointer to the object in this position from the list.
|
||||
Deal with all the chain pointers
|
||||
Return a pointer to the object removed from the list.
|
||||
The node object that is freed as a result
|
||||
of this operation is added to the node cache where
|
||||
it can be used again.
|
||||
Remove(NULL) is a harmless no-op - but probably is a wart.
|
||||
*/
|
||||
__out_opt void *CBaseList::RemoveI(__in_opt 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.
|
||||
*/
|
||||
|
||||
__out_opt POSITION CBaseList::AddTailI(__in void *pObject)
|
||||
{
|
||||
/* Lock the critical section before continuing */
|
||||
|
||||
CNode *pNode;
|
||||
// ASSERT(pObject); // NULL pointers in the list are allowed.
|
||||
|
||||
/* If there is a node objects in the cache then use
|
||||
that otherwise we will have to create a new one */
|
||||
|
||||
pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObject);
|
||||
pNode->SetNext(NULL);
|
||||
pNode->SetPrev(m_pLast);
|
||||
|
||||
if (m_pLast == NULL) {
|
||||
m_pFirst = pNode;
|
||||
} else {
|
||||
m_pLast->SetNext(pNode);
|
||||
}
|
||||
|
||||
/* Set the new last node pointer and also increment the number
|
||||
of list entries, the critical section is unlocked when we
|
||||
exit the function
|
||||
*/
|
||||
|
||||
m_pLast = pNode;
|
||||
++m_Count;
|
||||
|
||||
return (POSITION) pNode;
|
||||
} // AddTail(object)
|
||||
|
||||
|
||||
|
||||
/* Add this object to the head end of our list
|
||||
Return the new head position.
|
||||
*/
|
||||
__out_opt POSITION CBaseList::AddHeadI(__in void *pObject)
|
||||
{
|
||||
CNode *pNode;
|
||||
// ASSERT(pObject); // NULL pointers in the list are allowed.
|
||||
|
||||
/* If there is a node objects in the cache then use
|
||||
that otherwise we will have to create a new one */
|
||||
|
||||
pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObject);
|
||||
|
||||
/* chain it in (set four pointers) */
|
||||
pNode->SetPrev(NULL);
|
||||
pNode->SetNext(m_pFirst);
|
||||
|
||||
if (m_pFirst == NULL) {
|
||||
m_pLast = pNode;
|
||||
} else {
|
||||
m_pFirst->SetPrev(pNode);
|
||||
}
|
||||
m_pFirst = pNode;
|
||||
|
||||
++m_Count;
|
||||
|
||||
return (POSITION) pNode;
|
||||
} // AddHead(object)
|
||||
|
||||
|
||||
|
||||
/* Add all the elements in *pList to the tail of this list.
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails some elements may have been added.
|
||||
*/
|
||||
BOOL CBaseList::AddTail(__in CBaseList *pList)
|
||||
{
|
||||
/* lock the object before starting then enumerate
|
||||
each entry in the source list and add them one by one to
|
||||
our list (while still holding the object lock)
|
||||
Lock the other list too.
|
||||
*/
|
||||
POSITION pos = pList->GetHeadPositionI();
|
||||
|
||||
while (pos) {
|
||||
if (NULL == AddTailI(pList->GetNextI(pos))) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
} // AddTail(list)
|
||||
|
||||
|
||||
|
||||
/* Add all the elements in *pList to the head of this list.
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
If it fails some elements may have been added.
|
||||
*/
|
||||
BOOL CBaseList::AddHead(__in CBaseList *pList)
|
||||
{
|
||||
/* lock the object before starting then enumerate
|
||||
each entry in the source list and add them one by one to
|
||||
our list (while still holding the object lock)
|
||||
Lock the other list too.
|
||||
|
||||
To avoid reversing the list, traverse it backwards.
|
||||
*/
|
||||
|
||||
POSITION pos;
|
||||
|
||||
INTERNALREVERSETRAVERSELIST(*pList, pos) {
|
||||
if (NULL== AddHeadI(pList->GetValidI(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
|
||||
*/
|
||||
__out_opt POSITION CBaseList::AddAfterI(__in_opt POSITION pos, __in void * pObj)
|
||||
{
|
||||
if (pos==NULL)
|
||||
return AddHeadI(pObj);
|
||||
|
||||
/* As someone else might be furkling with the list -
|
||||
Lock the critical section before continuing
|
||||
*/
|
||||
CNode *pAfter = (CNode *) pos;
|
||||
ASSERT(pAfter != NULL);
|
||||
if (pAfter==m_pLast)
|
||||
return AddTailI(pObj);
|
||||
|
||||
/* set pnode to point to a new node, preferably from the cache */
|
||||
|
||||
CNode *pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObj);
|
||||
|
||||
/* It is to be added to the middle of the list - there is a before
|
||||
and after node. Chain it after pAfter, before pBefore.
|
||||
*/
|
||||
CNode * pBefore = pAfter->Next();
|
||||
ASSERT(pBefore != NULL);
|
||||
|
||||
/* chain it in (set four pointers) */
|
||||
pNode->SetPrev(pAfter);
|
||||
pNode->SetNext(pBefore);
|
||||
pBefore->SetPrev(pNode);
|
||||
pAfter->SetNext(pNode);
|
||||
|
||||
++m_Count;
|
||||
|
||||
return (POSITION) pNode;
|
||||
|
||||
} // AddAfter(object)
|
||||
|
||||
|
||||
|
||||
BOOL CBaseList::AddAfter(__in_opt POSITION p, __in CBaseList *pList)
|
||||
{
|
||||
POSITION pos;
|
||||
INTERNALTRAVERSELIST(*pList, pos) {
|
||||
/* p follows along the elements being added */
|
||||
p = AddAfterI(p, pList->GetValidI(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
|
||||
*/
|
||||
__out_opt POSITION CBaseList::AddBeforeI(__in_opt POSITION pos, __in void * pObj)
|
||||
{
|
||||
if (pos==NULL)
|
||||
return AddTailI(pObj);
|
||||
|
||||
/* set pnode to point to a new node, preferably from the cache */
|
||||
|
||||
CNode *pBefore = (CNode *) pos;
|
||||
ASSERT(pBefore != NULL);
|
||||
if (pBefore==m_pFirst)
|
||||
return AddHeadI(pObj);
|
||||
|
||||
CNode * pNode = (CNode *) m_Cache.RemoveFromCache();
|
||||
if (pNode == NULL) {
|
||||
pNode = new CNode;
|
||||
}
|
||||
|
||||
/* Check we have a valid object */
|
||||
|
||||
if (pNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise all the CNode object
|
||||
just in case it came from the cache
|
||||
*/
|
||||
|
||||
pNode->SetData(pObj);
|
||||
|
||||
/* It is to be added to the middle of the list - there is a before
|
||||
and after node. Chain it after pAfter, before pBefore.
|
||||
*/
|
||||
|
||||
CNode * pAfter = pBefore->Prev();
|
||||
ASSERT(pAfter != NULL);
|
||||
|
||||
/* chain it in (set four pointers) */
|
||||
pNode->SetPrev(pAfter);
|
||||
pNode->SetNext(pBefore);
|
||||
pBefore->SetPrev(pNode);
|
||||
pAfter->SetNext(pNode);
|
||||
|
||||
++m_Count;
|
||||
|
||||
return (POSITION) pNode;
|
||||
|
||||
} // Addbefore(object)
|
||||
|
||||
|
||||
|
||||
BOOL CBaseList::AddBefore(__in_opt POSITION p, __in CBaseList *pList)
|
||||
{
|
||||
POSITION pos;
|
||||
INTERNALREVERSETRAVERSELIST(*pList, pos) {
|
||||
/* p follows along the elements being added */
|
||||
p = AddBeforeI(p, pList->GetValidI(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
|
||||
(__in_opt POSITION pos, __in 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
|
||||
(__in_opt POSITION pos, __in 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
|
||||
|
||||
#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */
|
||||
+553
@@ -0,0 +1,553 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: WXList.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines a non-MFC generic template list
|
||||
// class.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/* A generic list of pointers to objects.
|
||||
No storage management or copying is done on the objects pointed to.
|
||||
Objectives: avoid using MFC libraries in ndm kernel mode and
|
||||
provide a really useful list type.
|
||||
|
||||
The class is thread safe in that separate threads may add and
|
||||
delete items in the list concurrently although the application
|
||||
must ensure that constructor and destructor access is suitably
|
||||
synchronised. An application can cause deadlock with operations
|
||||
which use two lists by simultaneously calling
|
||||
list1->Operation(list2) and list2->Operation(list1). So don't!
|
||||
|
||||
The names must not conflict with MFC classes as an application
|
||||
may use both.
|
||||
*/
|
||||
|
||||
#ifndef __WXLIST__
|
||||
#define __WXLIST__
|
||||
|
||||
/* A POSITION represents (in some fashion that's opaque) a cursor
|
||||
on the list that can be set to identify any element. NULL is
|
||||
a valid value and several operations regard NULL as the position
|
||||
"one step off the end of the list". (In an n element list there
|
||||
are n+1 places to insert and NULL is that "n+1-th" value).
|
||||
The POSITION of an element in the list is only invalidated if
|
||||
that element is deleted. Move operations may mean that what
|
||||
was a valid POSITION in one list is now a valid POSITION in
|
||||
a different list.
|
||||
|
||||
Some operations which at first sight are illegal are allowed as
|
||||
harmless no-ops. For instance RemoveHead is legal on an empty
|
||||
list and it returns NULL. This allows an atomic way to test if
|
||||
there is an element there, and if so, get it. The two operations
|
||||
AddTail and RemoveHead thus implement a MONITOR (See Hoare's paper).
|
||||
|
||||
Single element operations return POSITIONs, non-NULL means it worked.
|
||||
whole list operations return a BOOL. TRUE means it all worked.
|
||||
|
||||
This definition is the same as the POSITION type for MFCs, so we must
|
||||
avoid defining it twice.
|
||||
*/
|
||||
#ifndef __AFX_H__
|
||||
struct __POSITION { int unused; };
|
||||
typedef __POSITION* POSITION;
|
||||
#endif
|
||||
|
||||
const int DEFAULTCACHE = 10; /* Default node object cache size */
|
||||
|
||||
/* A class representing one node in a list.
|
||||
Each node knows a pointer to it's adjacent nodes and also a pointer
|
||||
to the object that it looks after.
|
||||
All of these pointers can be retrieved or set through member functions.
|
||||
*/
|
||||
class CBaseList
|
||||
#ifdef DEBUG
|
||||
: public CBaseObject
|
||||
#endif
|
||||
{
|
||||
/* Making these classes inherit from CBaseObject does nothing
|
||||
functionally but it allows us to check there are no memory
|
||||
leaks in debug builds.
|
||||
*/
|
||||
|
||||
public:
|
||||
|
||||
#ifdef DEBUG
|
||||
class CNode : public CBaseObject {
|
||||
#else
|
||||
class CNode {
|
||||
#endif
|
||||
|
||||
CNode *m_pPrev; /* Previous node in the list */
|
||||
CNode *m_pNext; /* Next node in the list */
|
||||
void *m_pObject; /* Pointer to the object */
|
||||
|
||||
public:
|
||||
|
||||
/* Constructor - initialise the object's pointers */
|
||||
CNode()
|
||||
#ifdef DEBUG
|
||||
: CBaseObject(NAME("List node"))
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/* Return the previous node before this one */
|
||||
__out CNode *Prev() const { return m_pPrev; };
|
||||
|
||||
|
||||
/* Return the next node after this one */
|
||||
__out CNode *Next() const { return m_pNext; };
|
||||
|
||||
|
||||
/* Set the previous node before this one */
|
||||
void SetPrev(__in_opt CNode *p) { m_pPrev = p; };
|
||||
|
||||
|
||||
/* Set the next node after this one */
|
||||
void SetNext(__in_opt CNode *p) { m_pNext = p; };
|
||||
|
||||
|
||||
/* Get the pointer to the object for this node */
|
||||
__out void *GetData() const { return m_pObject; };
|
||||
|
||||
|
||||
/* Set the pointer to the object for this node */
|
||||
void SetData(__in 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(__inout 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(__in_opt LPCTSTR pName,
|
||||
INT iItems);
|
||||
|
||||
CBaseList(__in_opt LPCTSTR pName);
|
||||
#ifdef UNICODE
|
||||
CBaseList(__in_opt LPCSTR pName,
|
||||
INT iItems);
|
||||
|
||||
CBaseList(__in_opt LPCSTR 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 */
|
||||
__out_opt POSITION GetHeadPositionI() const;
|
||||
|
||||
|
||||
/* Return a cursor which identifies the last element of *this */
|
||||
__out_opt 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.
|
||||
*/
|
||||
__out void *GetNextI(__inout POSITION& rp) const;
|
||||
|
||||
|
||||
/* Return a pointer to the object at p
|
||||
Asking for the object at NULL will return NULL harmlessly.
|
||||
*/
|
||||
__out_opt void *GetI(__in_opt POSITION p) const;
|
||||
__out void *GetValidI(__in POSITION p) const;
|
||||
|
||||
public:
|
||||
/* return the next / prev position in *this
|
||||
return NULL when going past the end/start.
|
||||
Next(NULL) is same as GetHeadPosition()
|
||||
Prev(NULL) is same as GetTailPosition()
|
||||
An n element list therefore behaves like a n+1 element
|
||||
cycle with NULL at the start/end.
|
||||
|
||||
!!WARNING!! - This handling of NULL is DIFFERENT from GetNext.
|
||||
|
||||
Some reasons are:
|
||||
1. For a list of n items there are n+1 positions to insert
|
||||
These are conveniently encoded as the n POSITIONs and NULL.
|
||||
2. If you are keeping a list sorted (fairly common) and you
|
||||
search forward for an element to insert before and don't
|
||||
find it you finish up with NULL as the element before which
|
||||
to insert. You then want that NULL to be a valid POSITION
|
||||
so that you can insert before it and you want that insertion
|
||||
point to mean the (n+1)-th one that doesn't have a POSITION.
|
||||
(symmetrically if you are working backwards through the list).
|
||||
3. It simplifies the algebra which the methods generate.
|
||||
e.g. AddBefore(p,x) is identical to AddAfter(Prev(p),x)
|
||||
in ALL cases. All the other arguments probably are reflections
|
||||
of the algebraic point.
|
||||
*/
|
||||
__out_opt POSITION Next(__in_opt POSITION pos) const
|
||||
{
|
||||
if (pos == NULL) {
|
||||
return (POSITION) m_pFirst;
|
||||
}
|
||||
CNode *pn = (CNode *) pos;
|
||||
return (POSITION) pn->Next();
|
||||
} //Next
|
||||
|
||||
// See Next
|
||||
__out_opt POSITION Prev(__in_opt POSITION pos) const
|
||||
{
|
||||
if (pos == NULL) {
|
||||
return (POSITION) m_pLast;
|
||||
}
|
||||
CNode *pn = (CNode *) pos;
|
||||
return (POSITION) pn->Prev();
|
||||
} //Prev
|
||||
|
||||
|
||||
/* Return the first position in *this which holds the given
|
||||
pointer. Return NULL if the pointer was not not found.
|
||||
*/
|
||||
protected:
|
||||
__out_opt POSITION FindI( __in void * pObj) const;
|
||||
|
||||
// ??? Should there be (or even should there be only)
|
||||
// ??? POSITION FindNextAfter(void * pObj, POSITION p)
|
||||
// ??? And of course FindPrevBefore too.
|
||||
// ??? List.Find(&Obj) then becomes List.FindNextAfter(&Obj, NULL)
|
||||
|
||||
|
||||
/* Remove the first node in *this (deletes the pointer to its
|
||||
object from the list, does not free the object itself).
|
||||
Return the pointer to its object.
|
||||
If *this was already empty it will harmlessly return NULL.
|
||||
*/
|
||||
__out_opt 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.
|
||||
*/
|
||||
__out_opt 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.
|
||||
*/
|
||||
__out_opt void *RemoveI(__in_opt 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
|
||||
*/
|
||||
__out_opt POSITION AddTailI(__in 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(__in 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:
|
||||
__out_opt POSITION AddHeadI(__in 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(__in 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:
|
||||
__out_opt POSITION AddAfterI(__in_opt POSITION p, __in 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(__in_opt POSITION p, __in 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:
|
||||
__out_opt POSITION AddBeforeI(__in_opt POSITION p, __in 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(__in_opt POSITION p, __in CBaseList *pList);
|
||||
|
||||
|
||||
/* Note that AddAfter(p,x) is equivalent to AddBefore(Next(p),x)
|
||||
even in cases where p is NULL or Next(p) is NULL.
|
||||
Similarly for mirror images etc.
|
||||
This may make it easier to argue about programs.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* The following operations do not copy any elements.
|
||||
They move existing blocks of elements around by switching pointers.
|
||||
They are fairly efficient for long lists as for short lists.
|
||||
(Alas, the Count slows things down).
|
||||
|
||||
They split the list into two parts.
|
||||
One part remains as the original list, the other part
|
||||
is appended to the second list. There are eight possible
|
||||
variations:
|
||||
Split the list {after/before} a given element
|
||||
keep the {head/tail} portion in the original list
|
||||
append the rest to the {head/tail} of the new list.
|
||||
|
||||
Since After is strictly equivalent to Before Next
|
||||
we are not in serious need of the Before/After variants.
|
||||
That leaves only four.
|
||||
|
||||
If you are processing a list left to right and dumping
|
||||
the bits that you have processed into another list as
|
||||
you go, the Tail/Tail variant gives the most natural result.
|
||||
If you are processing in reverse order, Head/Head is best.
|
||||
|
||||
By using NULL positions and empty lists judiciously either
|
||||
of the other two can be built up in two operations.
|
||||
|
||||
The definition of NULL (see Next/Prev etc) means that
|
||||
degenerate cases include
|
||||
"move all elements to new list"
|
||||
"Split a list into two lists"
|
||||
"Concatenate two lists"
|
||||
(and quite a few no-ops)
|
||||
|
||||
!!WARNING!! The type checking won't buy you much if you get list
|
||||
positions muddled up - e.g. use a POSITION that's in a different
|
||||
list and see what a mess you get!
|
||||
*/
|
||||
|
||||
/* Split *this after position p in *this
|
||||
Retain as *this the tail portion of the original *this
|
||||
Add the head portion to the tail end of *pList
|
||||
Return TRUE if it all worked, FALSE if it didn't.
|
||||
|
||||
e.g.
|
||||
foo->MoveToTail(foo->GetHeadPosition(), bar);
|
||||
moves one element from the head of foo to the tail of bar
|
||||
foo->MoveToTail(NULL, bar);
|
||||
is a no-op, returns NULL
|
||||
foo->MoveToTail(foo->GetTailPosition, bar);
|
||||
concatenates foo onto the end of bar and empties foo.
|
||||
|
||||
A better, except excessively long name might be
|
||||
MoveElementsFromHeadThroughPositionToOtherTail
|
||||
*/
|
||||
BOOL MoveToTail(__in_opt POSITION pos, __in 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(__in_opt POSITION pos, __in 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(__in_opt LPCTSTR pName,
|
||||
INT iItems,
|
||||
BOOL bLock = TRUE,
|
||||
BOOL bAlert = FALSE) :
|
||||
CBaseList(pName, iItems) {
|
||||
UNREFERENCED_PARAMETER(bAlert);
|
||||
UNREFERENCED_PARAMETER(bLock);
|
||||
};
|
||||
CGenericList(__in_opt LPCTSTR pName) :
|
||||
CBaseList(pName) {
|
||||
};
|
||||
|
||||
__out_opt POSITION GetHeadPosition() const { return (POSITION)m_pFirst; }
|
||||
__out_opt POSITION GetTailPosition() const { return (POSITION)m_pLast; }
|
||||
int GetCount() const { return m_Count; }
|
||||
|
||||
__out OBJECT *GetNext(__inout POSITION& rp) const { return (OBJECT *) GetNextI(rp); }
|
||||
|
||||
__out_opt OBJECT *Get(__in_opt POSITION p) const { return (OBJECT *) GetI(p); }
|
||||
__out OBJECT *GetValid(__in POSITION p) const { return (OBJECT *) GetValidI(p); }
|
||||
__out_opt OBJECT *GetHead() const { return Get(GetHeadPosition()); }
|
||||
|
||||
__out_opt OBJECT *RemoveHead() { return (OBJECT *) RemoveHeadI(); }
|
||||
|
||||
__out_opt OBJECT *RemoveTail() { return (OBJECT *) RemoveTailI(); }
|
||||
|
||||
__out_opt OBJECT *Remove(__in_opt POSITION p) { return (OBJECT *) RemoveI(p); }
|
||||
__out_opt POSITION AddBefore(__in_opt POSITION p, __in OBJECT * pObj) { return AddBeforeI(p, pObj); }
|
||||
__out_opt POSITION AddAfter(__in_opt POSITION p, __in OBJECT * pObj) { return AddAfterI(p, pObj); }
|
||||
__out_opt POSITION AddHead(__in OBJECT * pObj) { return AddHeadI(pObj); }
|
||||
__out_opt POSITION AddTail(__in OBJECT * pObj) { return AddTailI(pObj); }
|
||||
BOOL AddTail(__in CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddTail((CBaseList *) pList); }
|
||||
BOOL AddHead(__in CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddHead((CBaseList *) pList); }
|
||||
BOOL AddAfter(__in_opt POSITION p, __in CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddAfter(p, (CBaseList *) pList); };
|
||||
BOOL AddBefore(__in_opt POSITION p, __in CGenericList<OBJECT> *pList)
|
||||
{ return CBaseList::AddBefore(p, (CBaseList *) pList); };
|
||||
__out_opt POSITION Find( __in 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__ */
|
||||
|
||||
+775
@@ -0,0 +1,775 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: WXUtil.cpp
|
||||
//
|
||||
// Desc: DirectShow base classes - implements helper classes for building
|
||||
// multimedia filters.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <pjmedia-videodev/config.h>
|
||||
|
||||
#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
|
||||
|
||||
#include <streams.h>
|
||||
#define STRSAFE_NO_DEPRECATE
|
||||
#include <strsafe.h>
|
||||
|
||||
|
||||
// --- CAMEvent -----------------------
|
||||
CAMEvent::CAMEvent(BOOL fManualReset, __inout_opt HRESULT *phr)
|
||||
{
|
||||
m_hEvent = CreateEvent(NULL, fManualReset, FALSE, NULL);
|
||||
if (NULL == m_hEvent) {
|
||||
if (NULL != phr && SUCCEEDED(*phr)) {
|
||||
*phr = E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAMEvent::CAMEvent(__inout_opt HRESULT *phr)
|
||||
{
|
||||
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (NULL == m_hEvent) {
|
||||
if (NULL != phr && SUCCEEDED(*phr)) {
|
||||
*phr = E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAMEvent::~CAMEvent()
|
||||
{
|
||||
if (m_hEvent) {
|
||||
EXECUTE_ASSERT(CloseHandle(m_hEvent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- CAMMsgEvent -----------------------
|
||||
// One routine. The rest is handled in CAMEvent
|
||||
|
||||
CAMMsgEvent::CAMMsgEvent(__inout_opt HRESULT *phr) : CAMEvent(FALSE, phr)
|
||||
{
|
||||
}
|
||||
|
||||
BOOL CAMMsgEvent::WaitMsg(DWORD dwTimeout)
|
||||
{
|
||||
// wait for the event to be signalled, or for the
|
||||
// timeout (in MS) to expire. allow SENT messages
|
||||
// to be processed while we wait
|
||||
DWORD dwWait;
|
||||
DWORD dwStartTime = 0;
|
||||
|
||||
// set the waiting period.
|
||||
DWORD dwWaitTime = dwTimeout;
|
||||
|
||||
// the timeout will eventually run down as we iterate
|
||||
// processing messages. grab the start time so that
|
||||
// we can calculate elapsed times.
|
||||
if (dwWaitTime != INFINITE) {
|
||||
dwStartTime = timeGetTime();
|
||||
}
|
||||
|
||||
do {
|
||||
dwWait = MsgWaitForMultipleObjects(1,&m_hEvent,FALSE, dwWaitTime, QS_SENDMESSAGE);
|
||||
if (dwWait == WAIT_OBJECT_0 + 1) {
|
||||
MSG Message;
|
||||
PeekMessage(&Message,NULL,0,0,PM_NOREMOVE);
|
||||
|
||||
// If we have an explicit length of time to wait calculate
|
||||
// the next wake up point - which might be now.
|
||||
// If dwTimeout is INFINITE, it stays INFINITE
|
||||
if (dwWaitTime != INFINITE) {
|
||||
|
||||
DWORD dwElapsed = timeGetTime()-dwStartTime;
|
||||
|
||||
dwWaitTime =
|
||||
(dwElapsed >= dwTimeout)
|
||||
? 0 // wake up with WAIT_TIMEOUT
|
||||
: dwTimeout-dwElapsed;
|
||||
}
|
||||
}
|
||||
} while (dwWait == WAIT_OBJECT_0 + 1);
|
||||
|
||||
// return TRUE if we woke on the event handle,
|
||||
// FALSE if we timed out.
|
||||
return (dwWait == WAIT_OBJECT_0);
|
||||
}
|
||||
|
||||
// --- CAMThread ----------------------
|
||||
|
||||
|
||||
CAMThread::CAMThread(__inout_opt HRESULT *phr)
|
||||
: m_EventSend(TRUE, phr), // must be manual-reset for CheckRequest()
|
||||
m_EventComplete(FALSE, phr)
|
||||
{
|
||||
m_hThread = NULL;
|
||||
}
|
||||
|
||||
CAMThread::~CAMThread() {
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
// when the thread starts, it calls this function. We unwrap the 'this'
|
||||
//pointer and call ThreadProc.
|
||||
DWORD WINAPI
|
||||
CAMThread::InitialThreadProc(__inout LPVOID pv)
|
||||
{
|
||||
HRESULT hrCoInit = CAMThread::CoInitializeHelper();
|
||||
if(FAILED(hrCoInit)) {
|
||||
DbgLog((LOG_ERROR, 1, TEXT("CoInitializeEx failed.")));
|
||||
}
|
||||
|
||||
CAMThread * pThread = (CAMThread *) pv;
|
||||
|
||||
HRESULT hr = pThread->ThreadProc();
|
||||
|
||||
if(SUCCEEDED(hrCoInit)) {
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
BOOL
|
||||
CAMThread::Create()
|
||||
{
|
||||
DWORD threadid;
|
||||
|
||||
CAutoLock lock(&m_AccessLock);
|
||||
|
||||
if (ThreadExists()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_hThread = CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
CAMThread::InitialThreadProc,
|
||||
this,
|
||||
0,
|
||||
&threadid);
|
||||
|
||||
if (!m_hThread) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DWORD
|
||||
CAMThread::CallWorker(DWORD dwParam)
|
||||
{
|
||||
// lock access to the worker thread for scope of this object
|
||||
CAutoLock lock(&m_AccessLock);
|
||||
|
||||
if (!ThreadExists()) {
|
||||
return (DWORD) E_FAIL;
|
||||
}
|
||||
|
||||
// set the parameter
|
||||
m_dwParam = dwParam;
|
||||
|
||||
// signal the worker thread
|
||||
m_EventSend.Set();
|
||||
|
||||
// wait for the completion to be signalled
|
||||
m_EventComplete.Wait();
|
||||
|
||||
// done - this is the thread's return value
|
||||
return m_dwReturnVal;
|
||||
}
|
||||
|
||||
// Wait for a request from the client
|
||||
DWORD
|
||||
CAMThread::GetRequest()
|
||||
{
|
||||
m_EventSend.Wait();
|
||||
return m_dwParam;
|
||||
}
|
||||
|
||||
// is there a request?
|
||||
BOOL
|
||||
CAMThread::CheckRequest(__out_opt DWORD * pParam)
|
||||
{
|
||||
if (!m_EventSend.Check()) {
|
||||
return FALSE;
|
||||
} else {
|
||||
if (pParam) {
|
||||
*pParam = m_dwParam;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// reply to the request
|
||||
void
|
||||
CAMThread::Reply(DWORD dw)
|
||||
{
|
||||
m_dwReturnVal = dw;
|
||||
|
||||
// The request is now complete so CheckRequest should fail from
|
||||
// now on
|
||||
//
|
||||
// This event should be reset BEFORE we signal the client or
|
||||
// the client may Set it before we reset it and we'll then
|
||||
// reset it (!)
|
||||
|
||||
m_EventSend.Reset();
|
||||
|
||||
// Tell the client we're finished
|
||||
|
||||
m_EventComplete.Set();
|
||||
}
|
||||
|
||||
HRESULT CAMThread::CoInitializeHelper()
|
||||
{
|
||||
// call CoInitializeEx and tell OLE not to create a window (this
|
||||
// thread probably won't dispatch messages and will hang on
|
||||
// broadcast msgs o/w).
|
||||
//
|
||||
// If CoInitEx is not available, threads that don't call CoCreate
|
||||
// aren't affected. Threads that do will have to handle the
|
||||
// failure. Perhaps we should fall back to CoInitialize and risk
|
||||
// hanging?
|
||||
//
|
||||
|
||||
// older versions of ole32.dll don't have CoInitializeEx
|
||||
|
||||
HRESULT hr = E_FAIL;
|
||||
HINSTANCE hOle = GetModuleHandle(TEXT("ole32.dll"));
|
||||
if(hOle)
|
||||
{
|
||||
typedef HRESULT (STDAPICALLTYPE *PCoInitializeEx)(
|
||||
LPVOID pvReserved, DWORD dwCoInit);
|
||||
PCoInitializeEx pCoInitializeEx =
|
||||
(PCoInitializeEx)(GetProcAddress(hOle, "CoInitializeEx"));
|
||||
if(pCoInitializeEx)
|
||||
{
|
||||
hr = (*pCoInitializeEx)(0, COINIT_DISABLE_OLE1DDE );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// caller must load ole32.dll
|
||||
DbgBreak("couldn't locate ole32.dll");
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
// destructor for CMsgThread - cleans up any messages left in the
|
||||
// queue when the thread exited
|
||||
CMsgThread::~CMsgThread()
|
||||
{
|
||||
if (m_hThread != NULL) {
|
||||
WaitForSingleObject(m_hThread, INFINITE);
|
||||
EXECUTE_ASSERT(CloseHandle(m_hThread));
|
||||
}
|
||||
|
||||
POSITION pos = m_ThreadQueue.GetHeadPosition();
|
||||
while (pos) {
|
||||
CMsg * pMsg = m_ThreadQueue.GetNext(pos);
|
||||
delete pMsg;
|
||||
}
|
||||
m_ThreadQueue.RemoveAll();
|
||||
|
||||
if (m_hSem != NULL) {
|
||||
EXECUTE_ASSERT(CloseHandle(m_hSem));
|
||||
}
|
||||
}
|
||||
|
||||
BOOL
|
||||
CMsgThread::CreateThread(
|
||||
)
|
||||
{
|
||||
m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
|
||||
if (m_hSem == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_hThread = ::CreateThread(NULL, 0, DefaultThreadProc,
|
||||
(LPVOID)this, 0, &m_ThreadId);
|
||||
return m_hThread != NULL;
|
||||
}
|
||||
|
||||
|
||||
// This is the threads message pump. Here we get and dispatch messages to
|
||||
// clients thread proc until the client refuses to process a message.
|
||||
// The client returns a non-zero value to stop the message pump, this
|
||||
// value becomes the threads exit code.
|
||||
|
||||
DWORD WINAPI
|
||||
CMsgThread::DefaultThreadProc(
|
||||
__inout LPVOID lpParam
|
||||
)
|
||||
{
|
||||
CMsgThread *lpThis = (CMsgThread *)lpParam;
|
||||
CMsg msg;
|
||||
LRESULT lResult;
|
||||
|
||||
// !!!
|
||||
CoInitialize(NULL);
|
||||
|
||||
// allow a derived class to handle thread startup
|
||||
lpThis->OnThreadInit();
|
||||
|
||||
do {
|
||||
lpThis->GetThreadMsg(&msg);
|
||||
lResult = lpThis->ThreadMessageProc(msg.uMsg,msg.dwFlags,
|
||||
msg.lpParam, msg.pEvent);
|
||||
} while (lResult == 0L);
|
||||
|
||||
// !!!
|
||||
CoUninitialize();
|
||||
|
||||
return (DWORD)lResult;
|
||||
}
|
||||
|
||||
|
||||
// Block until the next message is placed on the list m_ThreadQueue.
|
||||
// copies the message to the message pointed to by *pmsg
|
||||
void
|
||||
CMsgThread::GetThreadMsg(__out CMsg *msg)
|
||||
{
|
||||
CMsg * pmsg = NULL;
|
||||
|
||||
// keep trying until a message appears
|
||||
while (TRUE) {
|
||||
{
|
||||
CAutoLock lck(&m_Lock);
|
||||
pmsg = m_ThreadQueue.RemoveHead();
|
||||
if (pmsg == NULL) {
|
||||
m_lWaiting++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// the semaphore will be signalled when it is non-empty
|
||||
WaitForSingleObject(m_hSem, INFINITE);
|
||||
}
|
||||
// copy fields to caller's CMsg
|
||||
*msg = *pmsg;
|
||||
|
||||
// this CMsg was allocated by the 'new' in PutThreadMsg
|
||||
delete pmsg;
|
||||
|
||||
}
|
||||
|
||||
// Helper function - convert int to WSTR
|
||||
void WINAPI IntToWstr(int i, __out_ecount(12) LPWSTR wstr)
|
||||
{
|
||||
#ifdef UNICODE
|
||||
if (FAILED(StringCchPrintf(wstr, 12, L"%d", i))) {
|
||||
wstr[0] = 0;
|
||||
}
|
||||
#else
|
||||
TCHAR temp[12];
|
||||
if (FAILED(StringCchPrintf(temp, NUMELMS(temp), "%d", i))) {
|
||||
wstr[0] = 0;
|
||||
} else {
|
||||
MultiByteToWideChar(CP_ACP, 0, temp, -1, wstr, 12);
|
||||
}
|
||||
#endif
|
||||
} // IntToWstr
|
||||
|
||||
|
||||
#define MEMORY_ALIGNMENT 4
|
||||
#define MEMORY_ALIGNMENT_LOG2 2
|
||||
#define MEMORY_ALIGNMENT_MASK MEMORY_ALIGNMENT - 1
|
||||
|
||||
void * __stdcall memmoveInternal(void * dst, const void * src, size_t count)
|
||||
{
|
||||
void * ret = dst;
|
||||
|
||||
#if defined(_X86_) && defined(_MSC_VER)
|
||||
if (dst <= src || (char *)dst >= ((char *)src + count)) {
|
||||
|
||||
/*
|
||||
* Non-Overlapping Buffers
|
||||
* copy from lower addresses to higher addresses
|
||||
*/
|
||||
_asm {
|
||||
mov esi,src
|
||||
mov edi,dst
|
||||
mov ecx,count
|
||||
cld
|
||||
mov edx,ecx
|
||||
and edx,MEMORY_ALIGNMENT_MASK
|
||||
shr ecx,MEMORY_ALIGNMENT_LOG2
|
||||
rep movsd
|
||||
or ecx,edx
|
||||
jz memmove_done
|
||||
rep movsb
|
||||
memmove_done:
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
/*
|
||||
* Overlapping Buffers
|
||||
* copy from higher addresses to lower addresses
|
||||
*/
|
||||
_asm {
|
||||
mov esi,src
|
||||
mov edi,dst
|
||||
mov ecx,count
|
||||
std
|
||||
add esi,ecx
|
||||
add edi,ecx
|
||||
dec esi
|
||||
dec edi
|
||||
rep movsb
|
||||
cld
|
||||
}
|
||||
}
|
||||
#else
|
||||
MoveMemory(dst, src, count);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
HRESULT AMSafeMemMoveOffset(
|
||||
__in_bcount(dst_size) void * dst,
|
||||
__in size_t dst_size,
|
||||
__in DWORD cb_dst_offset,
|
||||
__in_bcount(src_size) const void * src,
|
||||
__in size_t src_size,
|
||||
__in DWORD cb_src_offset,
|
||||
__in size_t count)
|
||||
{
|
||||
// prevent read overruns
|
||||
if( count + cb_src_offset < count || // prevent integer overflow
|
||||
count + cb_src_offset > src_size) // prevent read overrun
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
// prevent write overruns
|
||||
if( count + cb_dst_offset < count || // prevent integer overflow
|
||||
count + cb_dst_offset > dst_size) // prevent write overrun
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
memmoveInternal( (BYTE *)dst+cb_dst_offset, (BYTE *)src+cb_src_offset, count);
|
||||
return S_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
/******************************Public*Routine******************************\
|
||||
* Debug CCritSec helpers
|
||||
*
|
||||
* We provide debug versions of the Constructor, destructor, Lock and Unlock
|
||||
* routines. The debug code tracks who owns each critical section by
|
||||
* maintaining a depth count.
|
||||
*
|
||||
* History:
|
||||
*
|
||||
\**************************************************************************/
|
||||
|
||||
CCritSec::CCritSec()
|
||||
{
|
||||
InitializeCriticalSection(&m_CritSec);
|
||||
m_currentOwner = m_lockCount = 0;
|
||||
m_fTrace = FALSE;
|
||||
}
|
||||
|
||||
CCritSec::~CCritSec()
|
||||
{
|
||||
DeleteCriticalSection(&m_CritSec);
|
||||
}
|
||||
|
||||
void CCritSec::Lock()
|
||||
{
|
||||
UINT tracelevel=3;
|
||||
DWORD us = GetCurrentThreadId();
|
||||
DWORD currentOwner = m_currentOwner;
|
||||
if (currentOwner && (currentOwner != us)) {
|
||||
// already owned, but not by us
|
||||
if (m_fTrace) {
|
||||
DbgLog((LOG_LOCKING, 2, TEXT("Thread %d about to wait for lock %x owned by %d"),
|
||||
GetCurrentThreadId(), &m_CritSec, currentOwner));
|
||||
tracelevel=2;
|
||||
// if we saw the message about waiting for the critical
|
||||
// section we ensure we see the message when we get the
|
||||
// critical section
|
||||
}
|
||||
}
|
||||
EnterCriticalSection(&m_CritSec);
|
||||
if (0 == m_lockCount++) {
|
||||
// we now own it for the first time. Set owner information
|
||||
m_currentOwner = us;
|
||||
|
||||
if (m_fTrace) {
|
||||
DbgLog((LOG_LOCKING, tracelevel, TEXT("Thread %d now owns lock %x"), m_currentOwner, &m_CritSec));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCritSec::Unlock() {
|
||||
if (0 == --m_lockCount) {
|
||||
// about to be unowned
|
||||
if (m_fTrace) {
|
||||
DbgLog((LOG_LOCKING, 3, TEXT("Thread %d releasing lock %x"), m_currentOwner, &m_CritSec));
|
||||
}
|
||||
|
||||
m_currentOwner = 0;
|
||||
}
|
||||
LeaveCriticalSection(&m_CritSec);
|
||||
}
|
||||
|
||||
void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace)
|
||||
{
|
||||
pcCrit->m_fTrace = fTrace;
|
||||
}
|
||||
|
||||
BOOL WINAPI CritCheckIn(CCritSec * pcCrit)
|
||||
{
|
||||
return (GetCurrentThreadId() == pcCrit->m_currentOwner);
|
||||
}
|
||||
|
||||
BOOL WINAPI CritCheckIn(const CCritSec * pcCrit)
|
||||
{
|
||||
return (GetCurrentThreadId() == pcCrit->m_currentOwner);
|
||||
}
|
||||
|
||||
BOOL WINAPI CritCheckOut(CCritSec * pcCrit)
|
||||
{
|
||||
return (GetCurrentThreadId() != pcCrit->m_currentOwner);
|
||||
}
|
||||
|
||||
BOOL WINAPI CritCheckOut(const CCritSec * pcCrit)
|
||||
{
|
||||
return (GetCurrentThreadId() != pcCrit->m_currentOwner);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
STDAPI WriteBSTR(__deref_out BSTR *pstrDest, LPCWSTR szSrc)
|
||||
{
|
||||
*pstrDest = SysAllocString( szSrc );
|
||||
if( !(*pstrDest) ) return E_OUTOFMEMORY;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
STDAPI FreeBSTR(__deref_in BSTR* pstr)
|
||||
{
|
||||
if( (PVOID)*pstr == NULL ) return S_FALSE;
|
||||
SysFreeString( *pstr );
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
// Return a wide string - allocating memory for it
|
||||
// Returns:
|
||||
// S_OK - no error
|
||||
// E_POINTER - ppszReturn == NULL
|
||||
// E_OUTOFMEMORY - can't allocate memory for returned string
|
||||
STDAPI AMGetWideString(LPCWSTR psz, __deref_out LPWSTR *ppszReturn)
|
||||
{
|
||||
CheckPointer(ppszReturn, E_POINTER);
|
||||
ValidateReadWritePtr(ppszReturn, sizeof(LPWSTR));
|
||||
*ppszReturn = NULL;
|
||||
size_t nameLen;
|
||||
HRESULT hr = StringCbLengthW(psz, 100000, &nameLen);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
*ppszReturn = (LPWSTR)CoTaskMemAlloc(nameLen + sizeof(WCHAR));
|
||||
if (*ppszReturn == NULL) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
CopyMemory(*ppszReturn, psz, nameLen + sizeof(WCHAR));
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
// Waits for the HANDLE hObject. While waiting messages sent
|
||||
// to windows on our thread by SendMessage will be processed.
|
||||
// Using this function to do waits and mutual exclusion
|
||||
// avoids some deadlocks in objects with windows.
|
||||
// Return codes are the same as for WaitForSingleObject
|
||||
DWORD WINAPI WaitDispatchingMessages(
|
||||
HANDLE hObject,
|
||||
DWORD dwWait,
|
||||
HWND hwnd,
|
||||
UINT uMsg,
|
||||
HANDLE hEvent)
|
||||
{
|
||||
BOOL bPeeked = FALSE;
|
||||
DWORD dwResult;
|
||||
DWORD dwStart = 0;
|
||||
DWORD dwThreadPriority = THREAD_PRIORITY_HIGHEST;
|
||||
|
||||
static UINT uMsgId = 0;
|
||||
|
||||
HANDLE hObjects[2] = { hObject, hEvent };
|
||||
if (dwWait != INFINITE && dwWait != 0) {
|
||||
dwStart = GetTickCount();
|
||||
}
|
||||
for (; ; ) {
|
||||
DWORD nCount = NULL != hEvent ? 2 : 1;
|
||||
|
||||
// Minimize the chance of actually dispatching any messages
|
||||
// by seeing if we can lock immediately.
|
||||
dwResult = WaitForMultipleObjects(nCount, hObjects, FALSE, 0);
|
||||
if (dwResult < WAIT_OBJECT_0 + nCount) {
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD dwTimeOut = dwWait;
|
||||
if (dwTimeOut > 10) {
|
||||
dwTimeOut = 10;
|
||||
}
|
||||
dwResult = MsgWaitForMultipleObjects(
|
||||
nCount,
|
||||
hObjects,
|
||||
FALSE,
|
||||
dwTimeOut,
|
||||
hwnd == NULL ? QS_SENDMESSAGE :
|
||||
QS_SENDMESSAGE + QS_POSTMESSAGE);
|
||||
if (dwResult == WAIT_OBJECT_0 + nCount ||
|
||||
dwResult == WAIT_TIMEOUT && dwTimeOut != dwWait) {
|
||||
MSG msg;
|
||||
if (hwnd != NULL) {
|
||||
while (PeekMessage(&msg, hwnd, uMsg, uMsg, PM_REMOVE)) {
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
// Do this anyway - the previous peek doesn't flush out the
|
||||
// messages
|
||||
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
|
||||
|
||||
if (dwWait != INFINITE && dwWait != 0) {
|
||||
DWORD dwNow = GetTickCount();
|
||||
|
||||
// Working with differences handles wrap-around
|
||||
DWORD dwDiff = dwNow - dwStart;
|
||||
if (dwDiff > dwWait) {
|
||||
dwWait = 0;
|
||||
} else {
|
||||
dwWait -= dwDiff;
|
||||
}
|
||||
dwStart = dwNow;
|
||||
}
|
||||
if (!bPeeked) {
|
||||
// Raise our priority to prevent our message queue
|
||||
// building up
|
||||
dwThreadPriority = GetThreadPriority(GetCurrentThread());
|
||||
if (dwThreadPriority < THREAD_PRIORITY_HIGHEST) {
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
||||
}
|
||||
bPeeked = TRUE;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bPeeked) {
|
||||
SetThreadPriority(GetCurrentThread(), dwThreadPriority);
|
||||
if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {
|
||||
if (uMsgId == 0) {
|
||||
uMsgId = RegisterWindowMessage(TEXT("AMUnblock"));
|
||||
}
|
||||
if (uMsgId != 0) {
|
||||
MSG msg;
|
||||
// Remove old ones
|
||||
while (PeekMessage(&msg, (HWND)-1, uMsgId, uMsgId, PM_REMOVE)) {
|
||||
}
|
||||
}
|
||||
PostThreadMessage(GetCurrentThreadId(), uMsgId, 0, 0);
|
||||
}
|
||||
}
|
||||
return dwResult;
|
||||
}
|
||||
|
||||
HRESULT AmGetLastErrorToHResult()
|
||||
{
|
||||
DWORD dwLastError = GetLastError();
|
||||
if(dwLastError != 0)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(dwLastError);
|
||||
}
|
||||
else
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
IUnknown* QzAtlComPtrAssign(__deref_inout_opt IUnknown** pp, __in_opt IUnknown* lp)
|
||||
{
|
||||
if (lp != NULL)
|
||||
lp->AddRef();
|
||||
if (*pp)
|
||||
(*pp)->Release();
|
||||
*pp = lp;
|
||||
return lp;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
||||
CompatibleTimeSetEvent
|
||||
|
||||
CompatibleTimeSetEvent() sets the TIME_KILL_SYNCHRONOUS flag before calling
|
||||
timeSetEvent() if the current operating system supports it. TIME_KILL_SYNCHRONOUS
|
||||
is supported on Windows XP and later operating systems.
|
||||
|
||||
Parameters:
|
||||
- The same parameters as timeSetEvent(). See timeSetEvent()'s documentation in
|
||||
the Platform SDK for more information.
|
||||
|
||||
Return Value:
|
||||
- The same return value as timeSetEvent(). See timeSetEvent()'s documentation in
|
||||
the Platform SDK for more information.
|
||||
|
||||
******************************************************************************/
|
||||
MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent )
|
||||
{
|
||||
#if WINVER >= 0x0501
|
||||
{
|
||||
static bool fCheckedVersion = false;
|
||||
static bool fTimeKillSynchronousFlagAvailable = false;
|
||||
|
||||
if( !fCheckedVersion ) {
|
||||
fTimeKillSynchronousFlagAvailable = TimeKillSynchronousFlagAvailable();
|
||||
fCheckedVersion = true;
|
||||
}
|
||||
|
||||
if( fTimeKillSynchronousFlagAvailable ) {
|
||||
fuEvent = fuEvent | TIME_KILL_SYNCHRONOUS;
|
||||
}
|
||||
}
|
||||
#endif // WINVER >= 0x0501
|
||||
|
||||
return timeSetEvent( uDelay, uResolution, lpTimeProc, dwUser, fuEvent );
|
||||
}
|
||||
|
||||
bool TimeKillSynchronousFlagAvailable( void )
|
||||
{
|
||||
OSVERSIONINFO osverinfo;
|
||||
|
||||
osverinfo.dwOSVersionInfoSize = sizeof(osverinfo);
|
||||
|
||||
if( GetVersionEx( &osverinfo ) ) {
|
||||
|
||||
// Windows XP's major version is 5 and its' minor version is 1.
|
||||
// timeSetEvent() started supporting the TIME_KILL_SYNCHRONOUS flag
|
||||
// in Windows XP.
|
||||
if( (osverinfo.dwMajorVersion > 5) ||
|
||||
( (osverinfo.dwMajorVersion == 5) && (osverinfo.dwMinorVersion >= 1) ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */
|
||||
+534
@@ -0,0 +1,534 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// File: WXUtil.h
|
||||
//
|
||||
// Desc: DirectShow base classes - defines helper classes and functions for
|
||||
// building multimedia filters.
|
||||
//
|
||||
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __WXUTIL__
|
||||
#define __WXUTIL__
|
||||
|
||||
// eliminate spurious "statement has no effect" warnings.
|
||||
#pragma warning(disable: 4705)
|
||||
|
||||
// wrapper for whatever critical section we have
|
||||
class CCritSec {
|
||||
|
||||
// make copy constructor and assignment operator inaccessible
|
||||
|
||||
CCritSec(const CCritSec &refCritSec);
|
||||
CCritSec &operator=(const CCritSec &refCritSec);
|
||||
|
||||
CRITICAL_SECTION m_CritSec;
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
DWORD m_currentOwner;
|
||||
DWORD m_lockCount;
|
||||
BOOL m_fTrace; // Trace this one
|
||||
public:
|
||||
CCritSec();
|
||||
~CCritSec();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
#else
|
||||
|
||||
public:
|
||||
CCritSec() {
|
||||
InitializeCriticalSection(&m_CritSec);
|
||||
};
|
||||
|
||||
~CCritSec() {
|
||||
DeleteCriticalSection(&m_CritSec);
|
||||
};
|
||||
|
||||
void Lock() {
|
||||
EnterCriticalSection(&m_CritSec);
|
||||
};
|
||||
|
||||
void Unlock() {
|
||||
LeaveCriticalSection(&m_CritSec);
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
//
|
||||
// To make deadlocks easier to track it is useful to insert in the
|
||||
// code an assertion that says whether we own a critical section or
|
||||
// not. We make the routines that do the checking globals to avoid
|
||||
// having different numbers of member functions in the debug and
|
||||
// retail class implementations of CCritSec. In addition we provide
|
||||
// a routine that allows usage of specific critical sections to be
|
||||
// traced. This is NOT on by default - there are far too many.
|
||||
//
|
||||
|
||||
#ifdef DEBUG
|
||||
BOOL WINAPI CritCheckIn(CCritSec * pcCrit);
|
||||
BOOL WINAPI CritCheckIn(const CCritSec * pcCrit);
|
||||
BOOL WINAPI CritCheckOut(CCritSec * pcCrit);
|
||||
BOOL WINAPI CritCheckOut(const CCritSec * pcCrit);
|
||||
void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace);
|
||||
#else
|
||||
#define CritCheckIn(x) TRUE
|
||||
#define CritCheckOut(x) TRUE
|
||||
#define DbgLockTrace(pc, fT)
|
||||
#endif
|
||||
|
||||
|
||||
// locks a critical section, and unlocks it automatically
|
||||
// when the lock goes out of scope
|
||||
class CAutoLock {
|
||||
|
||||
// make copy constructor and assignment operator inaccessible
|
||||
|
||||
CAutoLock(const CAutoLock &refAutoLock);
|
||||
CAutoLock &operator=(const CAutoLock &refAutoLock);
|
||||
|
||||
protected:
|
||||
CCritSec * m_pLock;
|
||||
|
||||
public:
|
||||
CAutoLock(CCritSec * plock)
|
||||
{
|
||||
m_pLock = plock;
|
||||
m_pLock->Lock();
|
||||
};
|
||||
|
||||
~CAutoLock() {
|
||||
m_pLock->Unlock();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
// wrapper for event objects
|
||||
class CAMEvent
|
||||
{
|
||||
|
||||
// make copy constructor and assignment operator inaccessible
|
||||
|
||||
CAMEvent(const CAMEvent &refEvent);
|
||||
CAMEvent &operator=(const CAMEvent &refEvent);
|
||||
|
||||
protected:
|
||||
HANDLE m_hEvent;
|
||||
public:
|
||||
CAMEvent(BOOL fManualReset = FALSE, __inout_opt HRESULT *phr = NULL);
|
||||
CAMEvent(__inout_opt HRESULT *phr);
|
||||
~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:
|
||||
|
||||
CAMMsgEvent(__inout_opt HRESULT *phr = NULL);
|
||||
|
||||
// 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
|
||||
|
||||
#ifdef AM_NOVTABLE
|
||||
// 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(__inout_opt HRESULT *phr = NULL);
|
||||
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(__inout 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() {
|
||||
|
||||
// Disable warning: Conversion from LONG to PVOID of greater size
|
||||
#pragma warning(push)
|
||||
#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(__out_opt 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();
|
||||
};
|
||||
#endif // AM_NOVTABLE
|
||||
|
||||
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Ensures that memory is not read past the length source buffer
|
||||
// and that memory is not written past the length of the dst buffer
|
||||
// dst - buffer to copy to
|
||||
// dst_size - total size of destination buffer
|
||||
// cb_dst_offset - offset, first byte copied to dst+cb_dst_offset
|
||||
// src - buffer to copy from
|
||||
// src_size - total size of source buffer
|
||||
// cb_src_offset - offset, first byte copied from src+cb_src_offset
|
||||
// count - number of bytes to copy
|
||||
//
|
||||
// Returns:
|
||||
// S_OK - no error
|
||||
// E_INVALIDARG - values passed would lead to overrun
|
||||
HRESULT AMSafeMemMoveOffset(
|
||||
__in_bcount(dst_size) void * dst,
|
||||
__in size_t dst_size,
|
||||
__in DWORD cb_dst_offset,
|
||||
__in_bcount(src_size) const void * src,
|
||||
__in size_t src_size,
|
||||
__in DWORD cb_src_offset,
|
||||
__in size_t count);
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
void * __stdcall memmoveInternal(void *, const void *, size_t);
|
||||
|
||||
inline void * __cdecl memchrInternal(const void *buf, int chr, size_t cnt)
|
||||
{
|
||||
#if defined(_X86_) && defined(_MSC_VER)
|
||||
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, __out_ecount(12) LPWSTR wstr);
|
||||
|
||||
#define WstrToInt(sz) _wtoi(sz)
|
||||
#define atoiW(sz) _wtoi(sz)
|
||||
#define atoiA(sz) atoi(sz)
|
||||
|
||||
// These are available to help managing bitmap VIDEOINFOHEADER media structures
|
||||
|
||||
extern const DWORD bits555[3];
|
||||
extern const DWORD bits565[3];
|
||||
extern const DWORD bits888[3];
|
||||
|
||||
// These help convert between VIDEOINFOHEADER and BITMAPINFO structures
|
||||
|
||||
STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader);
|
||||
STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader);
|
||||
STDAPI_(WORD) GetBitCount(const GUID *pSubtype);
|
||||
|
||||
// strmbase.lib implements this for compatibility with people who
|
||||
// managed to link to this directly. we don't want to advertise it.
|
||||
//
|
||||
// STDAPI_(/* T */ CHAR *) GetSubtypeName(const GUID *pSubtype);
|
||||
|
||||
STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype);
|
||||
STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype);
|
||||
|
||||
#ifdef UNICODE
|
||||
#define GetSubtypeName GetSubtypeNameW
|
||||
#else
|
||||
#define GetSubtypeName GetSubtypeNameA
|
||||
#endif
|
||||
|
||||
STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader);
|
||||
STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader);
|
||||
|
||||
#ifdef __AMVIDEO__
|
||||
STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo);
|
||||
STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo);
|
||||
#endif // __AMVIDEO__
|
||||
|
||||
|
||||
// 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(__deref_out BSTR * pstrDest, LPCWSTR szSrc);
|
||||
STDAPI FreeBSTR(__deref_in 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, __deref_out 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(__deref_inout_opt IUnknown** pp, __in_opt 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, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent );
|
||||
bool TimeKillSynchronousFlagAvailable( void );
|
||||
|
||||
// Helper to replace lstrcpmi
|
||||
__inline int lstrcmpiLocaleIndependentW(LPCWSTR lpsz1, LPCWSTR lpsz2)
|
||||
{
|
||||
return CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lpsz1, -1, lpsz2, -1) - CSTR_EQUAL;
|
||||
}
|
||||
__inline int lstrcmpiLocaleIndependentA(LPCSTR lpsz1, LPCSTR lpsz2)
|
||||
{
|
||||
return CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, lpsz1, -1, lpsz2, -1) - CSTR_EQUAL;
|
||||
}
|
||||
|
||||
#endif /* __WXUTIL__ */
|
||||
Reference in new issue
Block a user