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>
1210 lines
30 KiB
C++
1210 lines
30 KiB
C++
//------------------------------------------------------------------------------
|
|
// File: Graph.cpp
|
|
//
|
|
// Desc: Sample code for BDA graph building.
|
|
//
|
|
// Copyright (c) 2000-2001, Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "graph.h"
|
|
|
|
// Constructor, initializes member variables
|
|
// and calls InitializeGraphBuilder
|
|
CBDAFilterGraph::CBDAFilterGraph() :
|
|
m_fGraphBuilt(FALSE),
|
|
m_fGraphRunning(FALSE),
|
|
m_NetworkType(ATSC),
|
|
m_lMajorChannel(-1),
|
|
m_lMinorChannel(-1),
|
|
m_lPhysicalChannel(46L),// 46 is an in house test channel - go ahead and change it
|
|
m_dwGraphRegister (0)
|
|
{
|
|
if(FAILED(InitializeGraphBuilder()))
|
|
m_fGraphFailure = TRUE;
|
|
else
|
|
m_fGraphFailure = FALSE;
|
|
}
|
|
|
|
// Destructor
|
|
CBDAFilterGraph::~CBDAFilterGraph()
|
|
{
|
|
if(m_fGraphRunning)
|
|
{
|
|
StopGraph();
|
|
}
|
|
|
|
if(m_fGraphBuilt || m_fGraphFailure)
|
|
{
|
|
TearDownGraph();
|
|
}
|
|
}
|
|
|
|
|
|
// Instantiate graph object for filter graph building
|
|
HRESULT
|
|
CBDAFilterGraph::InitializeGraphBuilder()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// we have a graph already
|
|
if (m_pFilterGraph)
|
|
return S_OK;
|
|
|
|
// create the filter graph
|
|
if (FAILED (hr = m_pFilterGraph.CoCreateInstance (CLSID_FilterGraph)))
|
|
{
|
|
ErrorMessageBox(TEXT("Couldn't CoCreate IGraphBuilder\n"));
|
|
m_fGraphFailure = TRUE;
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// BuildGraph sets up devices, adds and connects filters
|
|
HRESULT
|
|
CBDAFilterGraph::BuildGraph(NETWORK_TYPE NetType)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
m_NetworkType = NetType;
|
|
|
|
// if we have already have a filter graph, tear it down
|
|
if(m_fGraphBuilt)
|
|
{
|
|
if(m_fGraphRunning)
|
|
{
|
|
hr = StopGraph ();
|
|
}
|
|
|
|
hr = TearDownGraph ();
|
|
}
|
|
|
|
// STEP 1: load network provider first so that it can configure other
|
|
// filters, such as configuring the demux to sprout output pins.
|
|
// We also need to submit a tune request to the Network Provider so it will
|
|
// tune to a channel
|
|
if(FAILED (hr = LoadNetworkProvider()))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot load network provider\n"));
|
|
TearDownGraph();
|
|
m_fGraphFailure = true;
|
|
return hr;
|
|
}
|
|
|
|
hr = m_pNetworkProvider->QueryInterface(__uuidof (ITuner), reinterpret_cast <void**> (&m_pITuner));
|
|
if(FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("pNetworkProvider->QI: Can't QI for ITuner.\n"));
|
|
TearDownGraph();
|
|
m_fGraphFailure = true;
|
|
return hr;
|
|
}
|
|
|
|
// create a tune request to initialize the network provider
|
|
// before connecting other filters
|
|
CComPtr <IATSCChannelTuneRequest> pATSCTuneRequest;
|
|
if(FAILED (hr = CreateATSCTuneRequest(
|
|
m_lPhysicalChannel,
|
|
m_lMajorChannel,
|
|
m_lMinorChannel,
|
|
&pATSCTuneRequest
|
|
)))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot create tune request\n"));
|
|
TearDownGraph();
|
|
m_fGraphFailure = true;
|
|
return hr;
|
|
}
|
|
|
|
//submit the tune request to the network provider
|
|
hr = m_pITuner->put_TuneRequest(pATSCTuneRequest);
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot submit the tune request\n"));
|
|
TearDownGraph();
|
|
m_fGraphFailure = true;
|
|
return hr;
|
|
}
|
|
|
|
|
|
// STEP2: Load tuner device and connect to network provider
|
|
if(FAILED (hr = LoadFilter (
|
|
KSCATEGORY_BDA_NETWORK_TUNER,
|
|
&m_pTunerDevice,
|
|
m_pNetworkProvider,
|
|
TRUE
|
|
)))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot load tuner device and connect network provider\n"));
|
|
TearDownGraph();
|
|
m_fGraphFailure = true;
|
|
return hr;
|
|
}
|
|
|
|
// STEP3: Load tuner device and connect to demodulator device
|
|
if(FAILED (hr = LoadFilter (
|
|
KSCATEGORY_BDA_RECEIVER_COMPONENT,
|
|
&m_pDemodulatorDevice,
|
|
m_pTunerDevice,
|
|
TRUE
|
|
)))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot load capture device and connect tuner\n"));
|
|
TearDownGraph();
|
|
m_fGraphFailure = true;
|
|
return hr;
|
|
}
|
|
|
|
// Step4: Load capture device and connect to tuner device
|
|
if(FAILED (hr = LoadFilter (
|
|
KSCATEGORY_BDA_RECEIVER_COMPONENT,
|
|
&m_pCaptureDevice,
|
|
m_pDemodulatorDevice,
|
|
TRUE
|
|
)))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot load capture device and connect tuner\n"));
|
|
TearDownGraph();
|
|
m_fGraphFailure = true;
|
|
return hr;
|
|
}
|
|
|
|
// Step5: Load demux
|
|
if(FAILED (hr = LoadDemux()))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot load demux\n"));
|
|
TearDownGraph();
|
|
m_fGraphFailure = true;
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// this next call loads and connects filters associated with
|
|
// the demultiplexor. if you want to manually load individual
|
|
// filters such as audio and video decoders, use the code at
|
|
// the bottom of this file
|
|
//
|
|
#ifdef DEBUG
|
|
hr = AddGraphToRot (m_pFilterGraph, &m_dwGraphRegister);
|
|
if (FAILED(hr))
|
|
{
|
|
///ErrorMessageBox(TEXT("Failed to register filter graph with ROT! hr=0x%x"), hr);
|
|
m_dwGraphRegister = 0;
|
|
}
|
|
#endif
|
|
//MessageBox (NULL, _T(""), _T(""), MB_OK);
|
|
// Step6: Render demux pins
|
|
if(FAILED (hr = RenderDemux()))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot load demux\n"));
|
|
TearDownGraph();
|
|
m_fGraphFailure = true;
|
|
return hr;
|
|
}
|
|
|
|
m_fGraphBuilt = true;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Loads the correct tuning space based on NETWORK_TYPE that got
|
|
// passed into BuildGraph()
|
|
HRESULT
|
|
CBDAFilterGraph::LoadTuningSpace()
|
|
{
|
|
CComPtr <ITuningSpaceContainer> pITuningSpaceContainer;
|
|
|
|
// get the tuningspace container for all the tuning spaces from SYSTEM_TUNING_SPACES
|
|
HRESULT hr = pITuningSpaceContainer.CoCreateInstance(CLSID_SystemTuningSpaces);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Could not CoCreate SystemTuningSpaces\n"));
|
|
return hr;
|
|
}
|
|
|
|
CComVariant var (m_NetworkType);
|
|
|
|
hr = pITuningSpaceContainer->get_Item(var, &m_pITuningSpace);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Unable to retrieve Tuning Space\n"));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Creates an ATSC Tune Request
|
|
HRESULT
|
|
CBDAFilterGraph::CreateATSCTuneRequest(
|
|
LONG lPhysicalChannel,
|
|
LONG lMajorChannel,
|
|
LONG lMinorChannel,
|
|
IATSCChannelTuneRequest** pTuneRequest
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pTuneRequest == NULL)
|
|
{
|
|
ErrorMessageBox (TEXT("Invalid pointer\n"));
|
|
return E_POINTER;
|
|
}
|
|
|
|
// Making sure we have a valid tuning space
|
|
if (m_pITuningSpace == NULL)
|
|
{
|
|
ErrorMessageBox(TEXT("Tuning Space is NULL\n"));
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Create an instance of the ATSC tuning space
|
|
CComQIPtr <IATSCTuningSpace> pATSCTuningSpace (m_pITuningSpace);
|
|
if (!pATSCTuningSpace)
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot QI for an IATSCTuningSpace\n"));
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Create an empty tune request.
|
|
CComPtr <ITuneRequest> pNewTuneRequest;
|
|
hr = pATSCTuningSpace->CreateTuneRequest(&pNewTuneRequest);
|
|
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("CreateTuneRequest: Can't create tune request.\n"));
|
|
return hr;
|
|
}
|
|
|
|
//query for an IATSCChannelTuneRequest interface pointer
|
|
CComQIPtr <IATSCChannelTuneRequest> pATSCTuneRequest (pNewTuneRequest);
|
|
if (!pATSCTuneRequest)
|
|
{
|
|
ErrorMessageBox(TEXT("CreateATSCTuneRequest: Can't QI for IATSCChannelTuneRequest.\n"));
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Set the initial major and minor channels
|
|
hr = pATSCTuneRequest->put_Channel(lMajorChannel);
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox(TEXT("put_Channel failed\n"));
|
|
return hr;
|
|
}
|
|
|
|
hr = pATSCTuneRequest->put_MinorChannel(lMinorChannel);
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox(TEXT("put_MinorChannel failed\n"));
|
|
return hr;
|
|
}
|
|
|
|
CComPtr <IATSCLocator> pATSCLocator;
|
|
hr = pATSCLocator.CoCreateInstance (CLSID_ATSCLocator);
|
|
if (FAILED( hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot create the ATSC locator failed\n"));
|
|
return hr;
|
|
}
|
|
|
|
// Set the initial physical channel.
|
|
//
|
|
hr = pATSCLocator->put_PhysicalChannel (lPhysicalChannel);
|
|
if (FAILED( hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot put the physical channel\n"));
|
|
return hr;
|
|
}
|
|
|
|
hr = pATSCTuneRequest->put_Locator (pATSCLocator);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot put the locator\n"));
|
|
return hr;
|
|
}
|
|
|
|
hr = pATSCTuneRequest.QueryInterface (pTuneRequest);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// LoadNetworkProvider loads network provider
|
|
HRESULT
|
|
CBDAFilterGraph::LoadNetworkProvider()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComBSTR bstrNetworkType;
|
|
CLSID CLSIDNetworkType;
|
|
|
|
// obtain tuning space then load network provider
|
|
if(m_pITuningSpace == NULL)
|
|
{
|
|
hr = LoadTuningSpace();
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot load TuningSpace\n"));
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
// Get the current Network Type clsid
|
|
hr = m_pITuningSpace->get_NetworkType(&bstrNetworkType);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("ITuningSpace::Get Network Type failed\n"));
|
|
return hr;
|
|
}
|
|
|
|
hr = CLSIDFromString(bstrNetworkType, &CLSIDNetworkType);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Couldn't get CLSIDFromString\n"));
|
|
return hr;
|
|
}
|
|
|
|
// create the network provider based on the clsid obtained from the tuning space
|
|
hr = CoCreateInstance(CLSIDNetworkType, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IBaseFilter, reinterpret_cast<void**>(&m_pNetworkProvider));
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Couldn't CoCreate Network Provider\n"));
|
|
return hr;
|
|
}
|
|
|
|
//add the Network Provider filter to the graph
|
|
hr = m_pFilterGraph->AddFilter(m_pNetworkProvider, L"Network Provider");
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// enumerates through registered filters
|
|
// instantiates the the filter object and adds it to the graph
|
|
// it checks to see if it connects to upstream filter
|
|
// if not, on to the next enumerated filter
|
|
// used for tuner, capture, MPE Data Filters and decoders that
|
|
// could have more than one filter object
|
|
// if pUpstreamFilter is NULL don't bother connecting
|
|
HRESULT
|
|
CBDAFilterGraph::LoadFilter(
|
|
REFCLSID clsid,
|
|
IBaseFilter** ppFilter,
|
|
IBaseFilter* pConnectFilter,
|
|
BOOL fIsUpstream
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fFoundFilter = FALSE;
|
|
CComPtr <IMoniker> pIMoniker;
|
|
CComPtr <IEnumMoniker> pIEnumMoniker;
|
|
|
|
if (!m_pICreateDevEnum)
|
|
{
|
|
hr = m_pICreateDevEnum.CoCreateInstance(CLSID_SystemDeviceEnum);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("LoadFilter(): Cannot CoCreate ICreateDevEnum"));
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
// obtain the enumerator
|
|
hr = m_pICreateDevEnum->CreateClassEnumerator(clsid, &pIEnumMoniker, 0);
|
|
// the call can return S_FALSE if no moniker exists, so explicitly check S_OK
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("LoadFilter(): Cannot CreateClassEnumerator"));
|
|
return hr;
|
|
}
|
|
if (S_OK != hr) // Class not found
|
|
{
|
|
ErrorMessageBox(TEXT("LoadFilter(): Class not found, CreateClassEnumerator returned S_FALSE"));
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
// next filter
|
|
while(pIEnumMoniker->Next(1, &pIMoniker, 0) == S_OK)
|
|
{
|
|
// obtain filter's friendly name
|
|
CComPtr <IPropertyBag> pBag;
|
|
hr = pIMoniker->BindToStorage(
|
|
NULL,
|
|
NULL,
|
|
IID_IPropertyBag,
|
|
reinterpret_cast<void**>(&pBag)
|
|
);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
OutputDebugString (TEXT("LoadFilter(): Cannot BindToStorage"));
|
|
return hr;
|
|
}
|
|
|
|
CComVariant varBSTR;
|
|
hr = pBag->Read(L"FriendlyName", &varBSTR, NULL);
|
|
if(FAILED(hr))
|
|
{
|
|
OutputDebugString (TEXT("LoadFilter(): IPropertyBag->Read method failed"));
|
|
pIMoniker = NULL;
|
|
continue;
|
|
}
|
|
|
|
// bind the filter
|
|
CComPtr <IBaseFilter> pFilter;
|
|
hr = pIMoniker->BindToObject(
|
|
NULL,
|
|
NULL,
|
|
IID_IBaseFilter,
|
|
reinterpret_cast<void**>(&pFilter)
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
pIMoniker = NULL;
|
|
pFilter = NULL;
|
|
continue;
|
|
}
|
|
|
|
|
|
hr = m_pFilterGraph->AddFilter (pFilter, varBSTR.bstrVal);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugString (TEXT("Cannot add filter\n"));
|
|
return hr;
|
|
}
|
|
|
|
//MessageBox (NULL, _T(""), _T(""), MB_OK);
|
|
// test connections
|
|
// to upstream filter
|
|
if (pConnectFilter)
|
|
{
|
|
if(fIsUpstream)
|
|
{
|
|
hr = ConnectFilters (pConnectFilter, pFilter);
|
|
}
|
|
else
|
|
{
|
|
hr = ConnectFilters (pFilter, pConnectFilter);
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
//that's the filter we want
|
|
fFoundFilter = TRUE;
|
|
pFilter.QueryInterface (ppFilter);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
fFoundFilter = FALSE;
|
|
// that wasn't the the filter we wanted
|
|
// so unload and try the next one
|
|
hr = m_pFilterGraph->RemoveFilter(pFilter);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
OutputDebugString(TEXT("Failed unloading Filter\n"));
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fFoundFilter = TRUE;
|
|
pFilter.QueryInterface (ppFilter);
|
|
break;
|
|
}
|
|
|
|
pIMoniker = NULL;
|
|
pFilter = NULL;
|
|
} // while
|
|
return S_OK;
|
|
}
|
|
|
|
// loads the demux into the FilterGraph
|
|
HRESULT
|
|
CBDAFilterGraph::LoadDemux()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = CoCreateInstance(
|
|
CLSID_MPEG2Demultiplexer,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IBaseFilter,
|
|
reinterpret_cast<void**>(&m_pDemux)
|
|
);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Could not CoCreateInstance CLSID_MPEG2Demultiplexer\n"));
|
|
return hr;
|
|
}
|
|
|
|
hr = m_pFilterGraph->AddFilter(m_pDemux, L"Demux");
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Unable to add demux filter to graph\n"));
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// renders demux output pins
|
|
HRESULT
|
|
CBDAFilterGraph::RenderDemux()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr <IPin> pIPin;
|
|
CComPtr <IPin> pDownstreamPin;
|
|
CComPtr <IEnumPins> pIEnumPins;
|
|
PIN_DIRECTION direction;
|
|
|
|
if (!m_pDemux)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
// connect the demux to the capture device
|
|
hr = ConnectFilters (m_pCaptureDevice, m_pDemux);
|
|
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot connect demux to capture filter\n"));
|
|
return hr;
|
|
}
|
|
|
|
// load transform information filter and connect it to the demux
|
|
hr = LoadFilter (
|
|
KSCATEGORY_BDA_TRANSPORT_INFORMATION,
|
|
&m_pTIF,
|
|
m_pDemux,
|
|
TRUE
|
|
);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot load TIF\n"));
|
|
return hr;
|
|
}
|
|
|
|
// load multi protocol encapsulator
|
|
hr = LoadFilter (
|
|
KSCATEGORY_BDA_RECEIVER_COMPONENT,
|
|
&m_pMPE,
|
|
m_pDemux,
|
|
TRUE
|
|
);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot load MPE\n"));
|
|
return hr;
|
|
}
|
|
|
|
// load IP Sink
|
|
hr = LoadFilter (
|
|
KSCATEGORY_IP_SINK,
|
|
&m_pIPSink,
|
|
m_pMPE,
|
|
TRUE
|
|
);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot load IP Sink\n"));
|
|
return hr;
|
|
}
|
|
|
|
// render/connect the rest of the demux pins
|
|
hr = m_pDemux->EnumPins (&pIEnumPins);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot get the enumpins\n"));
|
|
return hr;
|
|
}
|
|
|
|
while(pIEnumPins->Next(1, &pIPin, 0) == S_OK)
|
|
{
|
|
hr = pIPin->QueryDirection(&direction);
|
|
|
|
if(direction == PINDIR_OUTPUT)
|
|
{
|
|
pIPin->ConnectedTo (&pDownstreamPin);
|
|
|
|
if(pDownstreamPin == NULL)
|
|
{
|
|
m_pFilterGraph->Render (pIPin);
|
|
}
|
|
|
|
pDownstreamPin = NULL;
|
|
}
|
|
pIPin = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// removes each filter from the graph
|
|
HRESULT
|
|
CBDAFilterGraph::TearDownGraph()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr <IBaseFilter> pFilter;
|
|
CComPtr <IEnumFilters> pIFilterEnum;
|
|
|
|
m_pITuningSpace = NULL;
|
|
|
|
if(m_fGraphBuilt || m_fGraphFailure)
|
|
{
|
|
// unload manually added filters
|
|
m_pFilterGraph->RemoveFilter(m_pIPSink);
|
|
m_pFilterGraph->RemoveFilter(m_pMPE);
|
|
m_pFilterGraph->RemoveFilter(m_pTIF);
|
|
m_pFilterGraph->RemoveFilter(m_pDemux);
|
|
m_pFilterGraph->RemoveFilter(m_pNetworkProvider);
|
|
m_pFilterGraph->RemoveFilter(m_pTunerDevice);
|
|
m_pFilterGraph->RemoveFilter(m_pCaptureDevice);
|
|
|
|
m_pIPSink = NULL;
|
|
m_pMPE = NULL;
|
|
m_pTIF = NULL;
|
|
m_pDemux = NULL;
|
|
m_pNetworkProvider = NULL;
|
|
m_pTunerDevice = NULL;
|
|
m_pDemodulatorDevice = NULL;
|
|
m_pCaptureDevice = NULL;
|
|
|
|
// now go unload rendered filters
|
|
hr = m_pFilterGraph->EnumFilters(&pIFilterEnum);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox(TEXT("TearDownGraph: cannot EnumFilters\n"));
|
|
return E_FAIL;
|
|
}
|
|
|
|
pIFilterEnum->Reset();
|
|
|
|
while(pIFilterEnum->Next(1, &pFilter, 0) == S_OK) // addrefs filter
|
|
{
|
|
hr = m_pFilterGraph->RemoveFilter(pFilter);
|
|
|
|
if (FAILED (hr))
|
|
return hr;
|
|
|
|
pIFilterEnum->Reset();
|
|
pFilter.Release ();
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (m_dwGraphRegister)
|
|
{
|
|
RemoveGraphFromRot(m_dwGraphRegister);
|
|
m_dwGraphRegister = 0;
|
|
}
|
|
#endif
|
|
|
|
m_fGraphBuilt = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// ConnectFilters is called from BuildGraph
|
|
// to enumerate and connect pins
|
|
HRESULT
|
|
CBDAFilterGraph::ConnectFilters(
|
|
IBaseFilter* pFilterUpstream,
|
|
IBaseFilter* pFilterDownstream
|
|
)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
CComPtr <IPin> pIPinUpstream;
|
|
|
|
|
|
PIN_INFO PinInfoUpstream;
|
|
PIN_INFO PinInfoDownstream;
|
|
|
|
// validate passed in filters
|
|
ASSERT (pFilterUpstream);
|
|
ASSERT (pFilterDownstream);
|
|
|
|
// grab upstream filter's enumerator
|
|
CComPtr <IEnumPins> pIEnumPinsUpstream;
|
|
hr = pFilterUpstream->EnumPins(&pIEnumPinsUpstream);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot Enumerate Upstream Filter's Pins\n"));
|
|
return hr;
|
|
}
|
|
|
|
// iterate through upstream filter's pins
|
|
while (pIEnumPinsUpstream->Next (1, &pIPinUpstream, 0) == S_OK)
|
|
{
|
|
hr = pIPinUpstream->QueryPinInfo (&PinInfoUpstream);
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot Obtain Upstream Filter's PIN_INFO\n"));
|
|
return hr;
|
|
}
|
|
|
|
CComPtr <IPin> pPinDown;
|
|
pIPinUpstream->ConnectedTo (&pPinDown);
|
|
|
|
// bail if pins are connected
|
|
// otherwise check direction and connect
|
|
if ((PINDIR_OUTPUT == PinInfoUpstream.dir) && (pPinDown == NULL))
|
|
{
|
|
// grab downstream filter's enumerator
|
|
CComPtr <IEnumPins> pIEnumPinsDownstream;
|
|
hr = pFilterDownstream->EnumPins (&pIEnumPinsDownstream);
|
|
|
|
// iterate through downstream filter's pins
|
|
CComPtr <IPin> pIPinDownstream;
|
|
while (pIEnumPinsDownstream->Next (1, &pIPinDownstream, 0) == S_OK)
|
|
{
|
|
// make sure it is an input pin
|
|
hr = pIPinDownstream->QueryPinInfo(&PinInfoDownstream);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
CComPtr <IPin> pPinUp;
|
|
pIPinDownstream->ConnectedTo (&pPinUp);
|
|
|
|
if ((PINDIR_INPUT == PinInfoDownstream.dir) && (pPinUp == NULL))
|
|
{
|
|
if (SUCCEEDED (m_pFilterGraph->Connect(
|
|
pIPinUpstream,
|
|
pIPinDownstream))
|
|
)
|
|
{
|
|
PinInfoDownstream.pFilter->Release();
|
|
PinInfoUpstream.pFilter->Release();
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
PinInfoDownstream.pFilter->Release();
|
|
pIPinDownstream = NULL;
|
|
} // while next downstream filter pin
|
|
|
|
//We are now back into the upstream pin loop
|
|
} // if output pin
|
|
|
|
pIPinUpstream = NULL;
|
|
PinInfoUpstream.pFilter->Release();
|
|
} // while next upstream filter pin
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
// RunGraph checks to see if a graph has been built
|
|
// if not it calls BuildGraph
|
|
// RunGraph then calls MediaCtrl-Run
|
|
HRESULT
|
|
CBDAFilterGraph::RunGraph()
|
|
{
|
|
// check to see if the graph is already running
|
|
if(m_fGraphRunning)
|
|
return S_OK;
|
|
|
|
HRESULT hr = S_OK;
|
|
if (m_pIMediaControl == NULL)
|
|
hr = m_pFilterGraph.QueryInterface (&m_pIMediaControl);
|
|
|
|
if (SUCCEEDED (hr))
|
|
{
|
|
// run the graph
|
|
hr = m_pIMediaControl->Run();
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
m_fGraphRunning = true;
|
|
}
|
|
else
|
|
{
|
|
// stop parts of the graph that ran
|
|
m_pIMediaControl->Stop();
|
|
ErrorMessageBox(TEXT("Cannot run graph\n"));
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// StopGraph calls MediaCtrl - Stop
|
|
HRESULT
|
|
CBDAFilterGraph::StopGraph()
|
|
{
|
|
// check to see if the graph is already stopped
|
|
if(m_fGraphRunning == false)
|
|
return S_OK;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
ASSERT (m_pIMediaControl);
|
|
// pause before stopping
|
|
hr = m_pIMediaControl->Pause();
|
|
|
|
// stop the graph
|
|
hr = m_pIMediaControl->Stop();
|
|
|
|
m_fGraphRunning = (FAILED (hr))?true:false;
|
|
return hr;
|
|
}
|
|
|
|
// Set our client area for viewing
|
|
//
|
|
// Note, what you're not seeing here is a call to
|
|
// IAMSreamCconfig's GetFormat to obtain the video
|
|
// format properties that would enable us to set
|
|
// the viewing window's size
|
|
HRESULT
|
|
CBDAFilterGraph::SetVideoWindow(
|
|
HWND hwndMain
|
|
)
|
|
{
|
|
CComPtr <IVideoWindow> pVideoWindow;
|
|
RECT rc;
|
|
INT cyBorder;
|
|
INT cy;
|
|
HRESULT hr = S_OK;
|
|
|
|
// get IVideoWindow interface
|
|
hr = m_pFilterGraph->QueryInterface (&pVideoWindow);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("QueryInterface IVideoWindow Failed\n"));
|
|
return hr;
|
|
}
|
|
|
|
hr = pVideoWindow->put_Owner (reinterpret_cast <LONG> (hwndMain));
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Unable to set video window\n"));
|
|
return hr;
|
|
}
|
|
|
|
hr = pVideoWindow->put_WindowStyle (WS_CHILD);
|
|
if (FAILED (hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Unable to set the style for the video window\n"));
|
|
return hr;
|
|
}
|
|
|
|
GetClientRect(hwndMain, &rc);
|
|
cyBorder = GetSystemMetrics(SM_CYBORDER);
|
|
cy = cyBorder;
|
|
rc.bottom -= cy;
|
|
hr = pVideoWindow->SetWindowPosition(
|
|
0,
|
|
0,
|
|
rc.right,
|
|
rc.bottom
|
|
);
|
|
hr = pVideoWindow->put_Visible (OATRUE);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CBDAFilterGraph::ChangeChannel(
|
|
LONG lMajorChannel,
|
|
LONG lMinorChannel
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
m_lMajorChannel = lMajorChannel;
|
|
m_lMinorChannel = lMinorChannel;
|
|
|
|
CComPtr <IScanningTuner> pIScanningTuner;
|
|
|
|
if (!m_pNetworkProvider)
|
|
{
|
|
ErrorMessageBox(TEXT("The FilterGraph is not yet built.\n"));
|
|
return E_FAIL;
|
|
}
|
|
|
|
hr = m_pNetworkProvider.QueryInterface (&pIScanningTuner);
|
|
if (FAILED(hr))
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot QI for IScanningTuner\n"));
|
|
return hr;
|
|
}
|
|
|
|
|
|
// create tune request
|
|
CComPtr <IATSCChannelTuneRequest> pTuneRequest;
|
|
hr = CreateATSCTuneRequest(
|
|
m_lPhysicalChannel,
|
|
lMajorChannel,
|
|
lMinorChannel,
|
|
&pTuneRequest
|
|
);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = m_pITuner->put_TuneRequest (pTuneRequest);
|
|
if (FAILED (hr))
|
|
ErrorMessageBox(TEXT("Cannot submit tune request\n"));
|
|
}
|
|
else
|
|
{
|
|
ErrorMessageBox(TEXT("Cannot Change Channels\n"));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// USE THE CODE BELOW IF YOU WANT TO MANUALLY LOAD AND
|
|
// CONNECT A/V DECODERS TO THE DEMUX OUTPUT PINS
|
|
//
|
|
|
|
/*
|
|
To use this code:
|
|
1) in LoadAudioDecoder() and LoadVideoDecoder(), fill in decoder specific information (clsid)
|
|
2) goto BuildGraph() and replace RenderDemux() with BuildAVSegment()
|
|
*/
|
|
|
|
/*
|
|
// Builds the Audio, Video segment of the digital TV graph.
|
|
// Demux -> AV Decoder -> OVMixer -> Video Renderer
|
|
HRESULT
|
|
CBDAFilterGraph::BuildAVSegment()
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// connect the demux to the capture device
|
|
hr = ConnectFilters(m_pCaptureDevice, m_pDemux);
|
|
|
|
hr = LoadVideoDecoder();
|
|
|
|
if(SUCCEEDED(hr) && m_pVideoDecoder)
|
|
{
|
|
// Connect the demux & video decoder
|
|
hr = ConnectFilters(m_pDemux, m_pVideoDecoder);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox("Connecting Demux & Video Decoder Failed\n");
|
|
goto err;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//ErrorMessageBox("Unable to load Video Decoder\n");
|
|
goto err;
|
|
}
|
|
|
|
//Audio
|
|
hr = LoadAudioDecoder();
|
|
|
|
if(SUCCEEDED(hr) && m_pAudioDecoder)
|
|
{
|
|
hr = ConnectFilters(m_pDemux, m_pAudioDecoder);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox("Connecting Deumx & Audio Decoder Failed\n");
|
|
goto err;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrorMessageBox("Unable to load Audio Decoder\n");
|
|
goto err;
|
|
}
|
|
|
|
// Create the OVMixer & Video Renderer for the video segment
|
|
hr = CoCreateInstance(CLSID_OverlayMixer, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IBaseFilter, reinterpret_cast<void**>(&m_pOVMixer));
|
|
|
|
if(SUCCEEDED(hr) && m_pOVMixer)
|
|
{
|
|
hr = m_pFilterGraph->AddFilter(m_pOVMixer, L"OVMixer");
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox("Adding OVMixer to the FilterGraph Failed\n");
|
|
goto err;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrorMessageBox("Loading OVMixer Failed\n");
|
|
goto err;
|
|
}
|
|
|
|
hr = CoCreateInstance(CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IBaseFilter, reinterpret_cast<void**>(&m_pVRenderer));
|
|
|
|
if(SUCCEEDED(hr) && m_pVRenderer)
|
|
{
|
|
hr = m_pFilterGraph->AddFilter(m_pVRenderer, L"Video Renderer");
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox("Adding Video Renderer to the FilterGraph Failed\n");
|
|
goto err;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrorMessageBox("Loading Video Renderer Failed\n");
|
|
goto err;
|
|
}
|
|
|
|
// Split AV Decoder? Then add Default DirectSound Renderer to the filtergraph
|
|
if(m_pVideoDecoder != m_pAudioDecoder)
|
|
{
|
|
hr = CoCreateInstance(CLSID_DSoundRender, NULL,
|
|
CLSCTX_INPROC_SERVER, IID_IBaseFilter,
|
|
reinterpret_cast<void**>(&m_pDDSRenderer));
|
|
|
|
if(SUCCEEDED(hr) && m_pDDSRenderer)
|
|
{
|
|
hr = m_pFilterGraph->AddFilter(m_pDDSRenderer, L"Sound Renderer");
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox("Adding DirectSound Device to the FilterGraph Failed\n");
|
|
goto err;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrorMessageBox("Loading DirectSound Device Failed\n");
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
hr = ConnectFilters(m_pVideoDecoder, m_pOVMixer);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox("Connecting Capture & OVMixer Failed\n");
|
|
goto err;
|
|
}
|
|
|
|
hr = ConnectFilters(m_pOVMixer, m_pVRenderer);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox("Connecting OVMixer & Video Renderer Failed\n");
|
|
goto err;
|
|
}
|
|
|
|
// Split AV Decoder & if you need audio too ?? then connect Audio decoder to Sound Renderer
|
|
if(m_pVideoDecoder != m_pAudioDecoder)
|
|
{
|
|
hr = ConnectFilters(m_pAudioDecoder, m_pDDSRenderer);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox("Connecting AudioDecoder & DirectSound Device Failed\n");
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
err:
|
|
return hr;
|
|
}
|
|
|
|
// placeholders for real decoders
|
|
DEFINE_GUID(CLSID_FILL_IN_NAME_AUDIO_DECODER, 0xFEEDFEED, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00);
|
|
DEFINE_GUID(CLSID_FILL_IN_NAME_VIDEO_DECODER, 0xFEEDFEED, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00);
|
|
|
|
HRESULT
|
|
CBDAFilterGraph::LoadVideoDecoder()
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
hr = CoCreateInstance(CLSID_FILL_IN_NAME_VIDEO_DECODER, NULL,
|
|
CLSCTX_INPROC_SERVER, IID_IBaseFilter,
|
|
reinterpret_cast<void**>(&m_pVideoDecoder));
|
|
|
|
if(SUCCEEDED(hr) && m_pVideoDecoder)
|
|
{
|
|
hr = m_pFilterGraph->AddFilter(m_pVideoDecoder, L"Video Decoder");
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox("Unable to add Video Decoder filter to graph\n");
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CBDAFilterGraph::LoadAudioDecoder()
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
hr = CoCreateInstance(CLSID_FILL_IN_NAME_AUDIO_DECODER, NULL,
|
|
CLSCTX_INPROC_SERVER, IID_IBaseFilter,
|
|
reinterpret_cast<void**>(&m_pAudioDecoder));
|
|
|
|
if(SUCCEEDED(hr) && m_pAudioDecoder)
|
|
{
|
|
hr = m_pFilterGraph->AddFilter(m_pAudioDecoder, L"Audio Decoder");
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
ErrorMessageBox("Unable to add Audio filter to graph\n");
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
*/
|
|
|
|
// Adds a DirectShow filter graph to the Running Object Table,
|
|
// allowing GraphEdit to "spy" on a remote filter graph.
|
|
HRESULT CBDAFilterGraph::AddGraphToRot(
|
|
IUnknown *pUnkGraph,
|
|
DWORD *pdwRegister
|
|
)
|
|
{
|
|
CComPtr <IMoniker> pMoniker;
|
|
CComPtr <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);
|
|
return hr;
|
|
}
|
|
|
|
// Removes a filter graph from the Running Object Table
|
|
void CBDAFilterGraph::RemoveGraphFromRot(
|
|
DWORD pdwRegister
|
|
)
|
|
{
|
|
CComPtr <IRunningObjectTable> pROT;
|
|
|
|
if (SUCCEEDED(GetRunningObjectTable(0, &pROT)))
|
|
pROT->Revoke(pdwRegister);
|
|
|
|
}
|
|
|