Game client codebase including: - CharacterActionControl: Character and creature management - GlobalScript: Network, items, skills, quests, utilities - RYLClient: Main client application with GUI and event handlers - Engine: 3D rendering engine (RYLGL) - MemoryManager: Custom memory allocation - Library: Third-party dependencies (DirectX, boost, etc.) - Tools: Development utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
619 lines
15 KiB
C++
619 lines
15 KiB
C++
//------------------------------------------------------------------------------
|
|
// File: DShowUtil.cpp
|
|
//
|
|
// Desc: DirectShow sample code - utility functions.
|
|
//
|
|
// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "stdafx.h"
|
|
#include <atlbase.h>
|
|
#include <dshow.h>
|
|
#include <mtype.h>
|
|
#include <wxdebug.h>
|
|
#include <reftime.h>
|
|
|
|
#include "dshowutil.h"
|
|
|
|
|
|
HRESULT FindRenderer(IGraphBuilder *pGB, const GUID *mediatype, IBaseFilter **ppFilter)
|
|
{
|
|
HRESULT hr;
|
|
IEnumFilters *pEnum = NULL;
|
|
IBaseFilter *pFilter = NULL;
|
|
IPin *pPin;
|
|
ULONG ulFetched, ulInPins, ulOutPins;
|
|
BOOL bFound=FALSE;
|
|
|
|
// Verify graph builder interface
|
|
if (!pGB)
|
|
return E_NOINTERFACE;
|
|
|
|
// Verify that a media type was passed
|
|
if (!mediatype)
|
|
return E_POINTER;
|
|
|
|
// Clear the filter pointer in case there is no match
|
|
if (ppFilter)
|
|
*ppFilter = NULL;
|
|
|
|
// Get filter enumerator
|
|
hr = pGB->EnumFilters(&pEnum);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
pEnum->Reset();
|
|
|
|
// Enumerate all filters in the graph
|
|
while(!bFound && (pEnum->Next(1, &pFilter, &ulFetched) == S_OK))
|
|
{
|
|
#ifdef DEBUG
|
|
// Read filter name for debugging purposes
|
|
FILTER_INFO FilterInfo;
|
|
TCHAR szName[256];
|
|
|
|
hr = pFilter->QueryFilterInfo(&FilterInfo);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Show filter name in debugger
|
|
#ifdef UNICODE
|
|
lstrcpy(szName, FilterInfo.achName);
|
|
#else
|
|
WideCharToMultiByte(CP_ACP, 0, FilterInfo.achName, -1, szName, 256, 0, 0);
|
|
#endif
|
|
FilterInfo.pGraph->Release();
|
|
}
|
|
#endif
|
|
|
|
// Find a filter with one input and no output pins
|
|
hr = CountFilterPins(pFilter, &ulInPins, &ulOutPins);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
if ((ulInPins == 1) && (ulOutPins == 0))
|
|
{
|
|
// Get the first pin on the filter
|
|
pPin=0;
|
|
pPin = GetInPin(pFilter, 0);
|
|
|
|
// Read this pin's major media type
|
|
AM_MEDIA_TYPE type={0};
|
|
hr = pPin->ConnectionMediaType(&type);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
// Is this pin's media type the requested type?
|
|
// If so, then this is the renderer for which we are searching.
|
|
// Copy the interface pointer and return.
|
|
if (type.majortype == *mediatype)
|
|
{
|
|
// Found our filter
|
|
*ppFilter = pFilter;
|
|
bFound = TRUE;;
|
|
}
|
|
// This is not the renderer, so release the interface.
|
|
else
|
|
pFilter->Release();
|
|
|
|
// Delete memory allocated by ConnectionMediaType()
|
|
FreeMediaType(type);
|
|
}
|
|
else
|
|
{
|
|
// No match, so release the interface
|
|
pFilter->Release();
|
|
}
|
|
}
|
|
|
|
pEnum->Release();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT FindAudioRenderer(IGraphBuilder *pGB, IBaseFilter **ppFilter)
|
|
{
|
|
return FindRenderer(pGB, &MEDIATYPE_Audio, ppFilter);
|
|
}
|
|
|
|
HRESULT FindVideoRenderer(IGraphBuilder *pGB, IBaseFilter **ppFilter)
|
|
{
|
|
return FindRenderer(pGB, &MEDIATYPE_Video, ppFilter);
|
|
}
|
|
|
|
HRESULT CountFilterPins(IBaseFilter *pFilter, ULONG *pulInPins, ULONG *pulOutPins)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
IEnumPins *pEnum=0;
|
|
ULONG ulFound;
|
|
IPin *pPin;
|
|
|
|
// Verify input
|
|
if (!pFilter || !pulInPins || !pulOutPins)
|
|
return E_POINTER;
|
|
|
|
// Clear number of pins found
|
|
*pulInPins = 0;
|
|
*pulOutPins = 0;
|
|
|
|
// Get pin enumerator
|
|
hr = pFilter->EnumPins(&pEnum);
|
|
if(FAILED(hr))
|
|
return hr;
|
|
|
|
pEnum->Reset();
|
|
|
|
// Count every pin on the filter
|
|
while(S_OK == pEnum->Next(1, &pPin, &ulFound))
|
|
{
|
|
PIN_DIRECTION pindir = (PIN_DIRECTION)3;
|
|
|
|
hr = pPin->QueryDirection(&pindir);
|
|
|
|
if(pindir == PINDIR_INPUT)
|
|
(*pulInPins)++;
|
|
else
|
|
(*pulOutPins)++;
|
|
|
|
pPin->Release();
|
|
}
|
|
|
|
pEnum->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CountTotalFilterPins(IBaseFilter *pFilter, ULONG *pulPins)
|
|
{
|
|
HRESULT hr;
|
|
IEnumPins *pEnum=0;
|
|
ULONG ulFound;
|
|
IPin *pPin;
|
|
|
|
// Verify input
|
|
if (!pFilter || !pulPins)
|
|
return E_POINTER;
|
|
|
|
// Clear number of pins found
|
|
*pulPins = 0;
|
|
|
|
// Get pin enumerator
|
|
hr = pFilter->EnumPins(&pEnum);
|
|
if(FAILED(hr))
|
|
return hr;
|
|
|
|
// Count every pin on the filter, ignoring direction
|
|
while(S_OK == pEnum->Next(1, &pPin, &ulFound))
|
|
{
|
|
(*pulPins)++;
|
|
pPin->Release();
|
|
}
|
|
|
|
pEnum->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT GetPin( IBaseFilter * pFilter, PIN_DIRECTION dirrequired, int iNum, IPin **ppPin)
|
|
{
|
|
CComPtr< IEnumPins > pEnum;
|
|
*ppPin = NULL;
|
|
HRESULT hr = pFilter->EnumPins(&pEnum);
|
|
if(FAILED(hr))
|
|
return hr;
|
|
|
|
ULONG ulFound;
|
|
IPin *pPin;
|
|
hr = E_FAIL;
|
|
|
|
while(S_OK == pEnum->Next(1, &pPin, &ulFound))
|
|
{
|
|
PIN_DIRECTION pindir = (PIN_DIRECTION)3;
|
|
pPin->QueryDirection(&pindir);
|
|
if(pindir == dirrequired)
|
|
{
|
|
if(iNum == 0)
|
|
{
|
|
*ppPin = pPin;
|
|
// Found requested pin, so clear error
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
iNum--;
|
|
}
|
|
|
|
pPin->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
IPin * GetInPin( IBaseFilter * pFilter, int Num )
|
|
{
|
|
CComPtr< IPin > pComPin;
|
|
GetPin(pFilter, PINDIR_INPUT, Num, &pComPin);
|
|
return pComPin;
|
|
}
|
|
|
|
|
|
IPin * GetOutPin( IBaseFilter * pFilter, int Num )
|
|
{
|
|
CComPtr< IPin > pComPin;
|
|
GetPin(pFilter, PINDIR_OUTPUT, Num, &pComPin);
|
|
return pComPin;
|
|
}
|
|
|
|
|
|
HRESULT FindOtherSplitterPin(IPin *pPinIn, GUID guid, int nStream, IPin **ppSplitPin)
|
|
{
|
|
if (!ppSplitPin)
|
|
return E_POINTER;
|
|
|
|
CComPtr< IPin > pPinOut;
|
|
pPinOut = pPinIn;
|
|
|
|
while(pPinOut)
|
|
{
|
|
PIN_INFO ThisPinInfo;
|
|
pPinOut->QueryPinInfo(&ThisPinInfo);
|
|
if(ThisPinInfo.pFilter) ThisPinInfo.pFilter->Release();
|
|
|
|
pPinOut = NULL;
|
|
CComPtr< IEnumPins > pEnumPins;
|
|
ThisPinInfo.pFilter->EnumPins(&pEnumPins);
|
|
if(!pEnumPins)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// look at every pin on the current filter...
|
|
//
|
|
ULONG Fetched = 0;
|
|
while(1)
|
|
{
|
|
CComPtr< IPin > pPin;
|
|
Fetched = 0;
|
|
ASSERT(!pPin); // is it out of scope?
|
|
pEnumPins->Next(1, &pPin, &Fetched);
|
|
if(!Fetched)
|
|
{
|
|
break;
|
|
}
|
|
|
|
PIN_INFO pi;
|
|
pPin->QueryPinInfo(&pi);
|
|
if(pi.pFilter) pi.pFilter->Release();
|
|
|
|
// if it's an input pin...
|
|
//
|
|
if(pi.dir == PINDIR_INPUT)
|
|
{
|
|
// continue searching upstream from this pin
|
|
//
|
|
pPin->ConnectedTo(&pPinOut);
|
|
|
|
// a pin that supports the required media type is the
|
|
// splitter pin we are looking for! We are done
|
|
//
|
|
}
|
|
else
|
|
{
|
|
CComPtr< IEnumMediaTypes > pMediaEnum;
|
|
pPin->EnumMediaTypes(&pMediaEnum);
|
|
if(pMediaEnum)
|
|
{
|
|
Fetched = 0;
|
|
AM_MEDIA_TYPE *pMediaType;
|
|
pMediaEnum->Next(1, &pMediaType, &Fetched);
|
|
if(Fetched)
|
|
{
|
|
if(pMediaType->majortype == guid)
|
|
{
|
|
if(nStream-- == 0)
|
|
{
|
|
DeleteMediaType(pMediaType);
|
|
*ppSplitPin = pPin;
|
|
(*ppSplitPin)->AddRef();
|
|
return S_OK;
|
|
}
|
|
}
|
|
DeleteMediaType(pMediaType);
|
|
}
|
|
}
|
|
}
|
|
|
|
// go try the next pin
|
|
|
|
} // while
|
|
}
|
|
ASSERT(FALSE);
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
HRESULT SeekNextFrame( IMediaSeeking * pSeeking, double FPS, long Frame )
|
|
{
|
|
// try seeking by frames first
|
|
//
|
|
HRESULT hr = pSeeking->SetTimeFormat(&TIME_FORMAT_FRAME);
|
|
REFERENCE_TIME Pos = 0;
|
|
if(!FAILED(hr))
|
|
{
|
|
pSeeking->GetCurrentPosition(&Pos);
|
|
Pos++;
|
|
}
|
|
else
|
|
{
|
|
// couldn't seek by frames, use Frame and FPS to calculate time
|
|
//
|
|
Pos = REFERENCE_TIME(double( Frame * UNITS ) / FPS);
|
|
|
|
// add a half-frame to seek to middle of the frame
|
|
//
|
|
Pos += REFERENCE_TIME(double( UNITS ) * 0.5 / FPS);
|
|
}
|
|
|
|
hr = pSeeking->SetPositions(&Pos, AM_SEEKING_AbsolutePositioning,
|
|
NULL, AM_SEEKING_NoPositioning);
|
|
return hr;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// for debugging purposes
|
|
const INT iMAXLEVELS = 5; // Maximum debug categories
|
|
extern DWORD m_Levels[iMAXLEVELS]; // Debug level per category
|
|
#endif
|
|
|
|
|
|
void TurnOnDebugDllDebugging( )
|
|
{
|
|
#ifdef DEBUG
|
|
for(int i = 0 ; i < iMAXLEVELS ; i++)
|
|
{
|
|
m_Levels[i] = 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void DbgPrint( char * pText )
|
|
{
|
|
DbgLog(( LOG_TRACE, 1, "%s", pText ));
|
|
}
|
|
|
|
void ErrPrint( char * pText )
|
|
{
|
|
printf(pText);
|
|
return;
|
|
}
|
|
|
|
|
|
// Adds a DirectShow filter graph to the Running Object Table,
|
|
// allowing GraphEdit to "spy" on a remote filter graph.
|
|
HRESULT AddGraphToRot(IUnknown *pUnkGraph, DWORD *pdwRegister)
|
|
{
|
|
IMoniker * pMoniker;
|
|
IRunningObjectTable *pROT;
|
|
WCHAR wsz[128];
|
|
HRESULT hr;
|
|
|
|
if (FAILED(GetRunningObjectTable(0, &pROT)))
|
|
return E_FAIL;
|
|
|
|
wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR)pUnkGraph,
|
|
GetCurrentProcessId());
|
|
|
|
hr = CreateItemMoniker(L"!", wsz, &pMoniker);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pROT->Register(0, pUnkGraph, pMoniker, pdwRegister);
|
|
pMoniker->Release();
|
|
}
|
|
pROT->Release();
|
|
return hr;
|
|
}
|
|
|
|
// Removes a filter graph from the Running Object Table
|
|
void RemoveGraphFromRot(DWORD pdwRegister)
|
|
{
|
|
IRunningObjectTable *pROT;
|
|
|
|
if (SUCCEEDED(GetRunningObjectTable(0, &pROT)))
|
|
{
|
|
pROT->Revoke(pdwRegister);
|
|
pROT->Release();
|
|
}
|
|
}
|
|
|
|
|
|
void ShowFilenameByCLSID(REFCLSID clsid, TCHAR *szFilename)
|
|
{
|
|
HRESULT hr;
|
|
LPOLESTR strCLSID;
|
|
|
|
// Convert binary CLSID to a readable version
|
|
hr = StringFromCLSID(clsid, &strCLSID);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
TCHAR szKey[512];
|
|
CString strQuery(strCLSID);
|
|
|
|
// Create key name for reading filename registry
|
|
wsprintf(szKey, TEXT("Software\\Classes\\CLSID\\%s\\InprocServer32\0"),
|
|
strQuery);
|
|
|
|
// Free memory associated with strCLSID (allocated in StringFromCLSID)
|
|
CoTaskMemFree(strCLSID);
|
|
|
|
HKEY hkeyFilter=0;
|
|
DWORD dwSize=MAX_PATH;
|
|
BYTE szFile[MAX_PATH];
|
|
int rc=0;
|
|
|
|
// Open the CLSID key that contains information about the filter
|
|
rc = RegOpenKey(HKEY_LOCAL_MACHINE, szKey, &hkeyFilter);
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
rc = RegQueryValueEx(hkeyFilter, NULL, // Read (Default) value
|
|
NULL, NULL, szFile, &dwSize);
|
|
|
|
if (rc == ERROR_SUCCESS)
|
|
wsprintf(szFilename, TEXT("%s"), szFile);
|
|
else
|
|
wsprintf(szFilename, TEXT("<Unknown>\0"));
|
|
|
|
rc = RegCloseKey(hkeyFilter);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT GetFileDurationString(IMediaSeeking *pMS, TCHAR *szDuration)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (!pMS)
|
|
return E_NOINTERFACE;
|
|
if (!szDuration)
|
|
return E_POINTER;
|
|
|
|
// Initialize the display in case we can't read the duration
|
|
wsprintf(szDuration, TEXT("<00:00.000>"));
|
|
|
|
// Is media time supported for this file?
|
|
if (S_OK != pMS->IsFormatSupported(&TIME_FORMAT_MEDIA_TIME))
|
|
return E_NOINTERFACE;
|
|
|
|
// Read the time format to restore later
|
|
GUID guidOriginalFormat;
|
|
hr = pMS->GetTimeFormat(&guidOriginalFormat);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Ensure media time format for easy display
|
|
hr = pMS->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Read the file's duration
|
|
LONGLONG llDuration;
|
|
hr = pMS->GetDuration(&llDuration);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Return to the original format
|
|
if (guidOriginalFormat != TIME_FORMAT_MEDIA_TIME)
|
|
{
|
|
hr = pMS->SetTimeFormat(&guidOriginalFormat);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Convert the LONGLONG duration into human-readable format
|
|
unsigned long nTotalMS = (unsigned long) llDuration / 10000; // 100ns -> ms
|
|
int nMS = nTotalMS % 1000;
|
|
int nSeconds = nTotalMS / 1000;
|
|
int nMinutes = nSeconds / 60;
|
|
nSeconds %= 60;
|
|
|
|
// Update the string
|
|
wsprintf(szDuration, _T("%02dm:%02d.%03ds\0"), nMinutes, nSeconds, nMS);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
BOOL SupportsPropertyPage(IBaseFilter *pFilter)
|
|
{
|
|
HRESULT hr;
|
|
ISpecifyPropertyPages *pSpecify;
|
|
|
|
// Discover if this filter contains a property page
|
|
hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pSpecify->Release();
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
HRESULT ShowFilterPropertyPage(IBaseFilter *pFilter, HWND hwndParent)
|
|
{
|
|
HRESULT hr;
|
|
ISpecifyPropertyPages *pSpecify=0;
|
|
|
|
if (!pFilter)
|
|
return E_NOINTERFACE;
|
|
|
|
// Discover if this filter contains a property page
|
|
hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
do
|
|
{
|
|
FILTER_INFO FilterInfo;
|
|
hr = pFilter->QueryFilterInfo(&FilterInfo);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
CAUUID caGUID;
|
|
hr = pSpecify->GetPages(&caGUID);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
pSpecify->Release();
|
|
|
|
// Display the filter's property page
|
|
OleCreatePropertyFrame(
|
|
hwndParent, // Parent window
|
|
0, // x (Reserved)
|
|
0, // y (Reserved)
|
|
FilterInfo.achName, // Caption for the dialog box
|
|
1, // Number of filters
|
|
(IUnknown **)&pFilter, // Pointer to the filter
|
|
caGUID.cElems, // Number of property pages
|
|
caGUID.pElems, // Pointer to property page CLSIDs
|
|
0, // Locale identifier
|
|
0, // Reserved
|
|
NULL // Reserved
|
|
);
|
|
CoTaskMemFree(caGUID.pElems);
|
|
FilterInfo.pGraph->Release();
|
|
|
|
} while(0);
|
|
}
|
|
|
|
pFilter->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Some hardware decoders and video renderers support stepping media
|
|
// frame by frame with the IVideoFrameStep interface. See the interface
|
|
// documentation for more details on frame stepping.
|
|
//
|
|
BOOL CanFrameStep(IGraphBuilder *pGB)
|
|
{
|
|
HRESULT hr;
|
|
IVideoFrameStep* pFS;
|
|
|
|
hr = pGB->QueryInterface(__uuidof(IVideoFrameStep), (PVOID *)&pFS);
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
|
|
// Check if this decoder can step
|
|
hr = pFS->CanStep(0L, NULL);
|
|
|
|
pFS->Release();
|
|
|
|
if (hr == S_OK)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|