mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3525 lines
121 KiB
3525 lines
121 KiB
// Copyright (c) 1994 - 1998 Microsoft Corporation. All Rights Reserved.
|
|
|
|
#include <streams.h>
|
|
#include <vfwmsgs.h>
|
|
|
|
#ifdef FILTER_DLL
|
|
// define the GUIDs for streams and my CLSID in this file
|
|
#include <initguid.h>
|
|
#endif
|
|
|
|
#include <dvdmedia.h>
|
|
#include <il21dec.h>
|
|
#include "dvdgb.h"
|
|
|
|
// setup data
|
|
|
|
#ifdef FILTER_DLL
|
|
// list of class ids and creator functions for class factory
|
|
CFactoryTemplate g_Templates[] = {
|
|
{ L"DVD Graph Builder"
|
|
, &CLSID_DvdGraphBuilder
|
|
, CDvdGraphBuilder::CreateInstance
|
|
, NULL
|
|
, NULL } // self-registering info
|
|
};
|
|
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
|
|
|
|
// exported entry points for registration and
|
|
// unregistration (in this case they only call
|
|
// through to default implmentations).
|
|
//
|
|
STDAPI DllRegisterServer()
|
|
{
|
|
return AMovieDllRegisterServer2( TRUE );
|
|
}
|
|
|
|
STDAPI DllUnregisterServer()
|
|
{
|
|
return AMovieDllRegisterServer2( FALSE );
|
|
}
|
|
#endif
|
|
|
|
|
|
CDvdGraphBuilder::CDvdGraphBuilder(TCHAR *pName, LPUNKNOWN pUnk, HRESULT * phr)
|
|
: CUnknown(pName, pUnk),
|
|
m_pGB(NULL),
|
|
m_Decoders(),
|
|
m_pDVDSrc(NULL),
|
|
m_pDVDNav(NULL),
|
|
m_pVR(NULL),
|
|
m_pAR(NULL),
|
|
m_pVM(NULL),
|
|
m_pL21Dec(NULL),
|
|
m_bGraphDone(FALSE),
|
|
m_bUseVPE(TRUE),
|
|
m_dwVideoRenderStatus(0),
|
|
m_pL21PinToRender(NULL),
|
|
m_pSPPinToRender(NULL)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("CDvdGraphBuilder::CDvdGraphBuilder()"))) ;
|
|
|
|
*phr = CreateGraph() ;
|
|
ZeroMemory(m_achwPathName, MAX_PATH * sizeof(m_achwPathName[0])) ;
|
|
}
|
|
|
|
|
|
CDvdGraphBuilder::~CDvdGraphBuilder()
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("CDvdGraphBuilder::~CDvdGraphBuilder() entering"))) ;
|
|
|
|
// If we have a graph object
|
|
if (m_pGB)
|
|
{
|
|
StopGraph() ; // make sure the graph is REALYY stopped
|
|
|
|
// Break the connections and remove all the filters we added from the graph
|
|
ClearGraph() ;
|
|
|
|
m_pGB->Release() ; // free it
|
|
m_pGB = NULL ;
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// Get a list of decoders in use in the graph and release them.
|
|
// Then free the memory for the list of decoders.
|
|
//
|
|
IBaseFilter *pDecList ;
|
|
int n = m_Decoders.GetList(&pDecList) ;
|
|
for (int i = 0 ; i < n ; n++)
|
|
(pDecList + i)->Release() ;
|
|
if (pDecList)
|
|
CoTaskMemFree(pDecList) ;
|
|
#endif // #if 0
|
|
|
|
DbgLog((LOG_TRACE, 3, TEXT("CDvdGraphBuilder::~CDvdGraphBuilder() ending"))) ;
|
|
}
|
|
|
|
|
|
void CDvdGraphBuilder::StopGraph(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::StopGraph()"))) ;
|
|
|
|
// Just paranoia
|
|
if (NULL == m_pGB)
|
|
{
|
|
ASSERT(FALSE) ; // so that we know
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: How are we doing a Stop w/o a graph???"))) ;
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Check that the graph has stopped; otherwise stop it here. Because a
|
|
// playing graph can't be cleaned up or rebuilt.
|
|
//
|
|
IMediaControl *pMC ;
|
|
LONG lState ;
|
|
HRESULT hr = m_pGB->QueryInterface(IID_IMediaControl, (LPVOID *)&pMC) ;
|
|
ASSERT(SUCCEEDED(hr) && pMC) ;
|
|
pMC->GetState(INFINITE, &lState) ;
|
|
if (State_Stopped != lState)
|
|
{
|
|
hr = pMC->Stop() ;
|
|
ASSERT(SUCCEEDED(hr)) ;
|
|
while (State_Stopped != lState)
|
|
{
|
|
Sleep(10) ;
|
|
hr = pMC->GetState(INFINITE, &lState) ;
|
|
ASSERT(SUCCEEDED(hr)) ;
|
|
}
|
|
}
|
|
pMC->Release() ;
|
|
DbgLog((LOG_TRACE, 4, TEXT("DVD-Video playback graph has stopped"))) ;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDvdGraphBuilder::NonDelegatingQueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::NonDelegatingQueryInterface()"))) ;
|
|
if (ppv)
|
|
*ppv = NULL;
|
|
|
|
if (riid == IID_IDvdGraphBuilder)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("QI for IDvdGraphBuilder"))) ;
|
|
return GetInterface((IDvdGraphBuilder *) this, ppv) ;
|
|
}
|
|
else // more interfaces
|
|
{
|
|
return CUnknown::NonDelegatingQueryInterface(riid, ppv) ;
|
|
}
|
|
}
|
|
|
|
|
|
// this goes in the factory template table to create new instances
|
|
CUnknown * CDvdGraphBuilder::CreateInstance(LPUNKNOWN pUnk, HRESULT * phr)
|
|
{
|
|
return new CDvdGraphBuilder(TEXT("DVD Graph Builder"), pUnk, phr) ;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////
|
|
// IDvdGraphBuilder stuff
|
|
///////////////////////
|
|
|
|
#if 0
|
|
//
|
|
// Use my filtergraph to build your DVD playback graph
|
|
//
|
|
HRESULT CDvdGraphBuilder::SetFiltergraph(IGraphBuilder *pGB)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("CDvdGraphBuilder::SetFiltergraph(0x%lx)"), pGB)) ;
|
|
|
|
if (m_pGB) // we already have a graph
|
|
return E_UNEXPECTED ; // no thanks.
|
|
|
|
if (pGB == NULL)
|
|
return E_POINTER ;
|
|
|
|
m_pGB = pGB ;
|
|
m_pGB->AddRef() ; // we own a copy now
|
|
|
|
//
|
|
// If we want to bother about the filters that might have been added to the
|
|
// passed in filter graph, we could enumerated and list them. But ignore that
|
|
// for now at least.
|
|
//
|
|
|
|
return NOERROR ;
|
|
}
|
|
#endif // #if 0
|
|
|
|
//
|
|
// What filtergraph is graph building being done in?
|
|
//
|
|
HRESULT CDvdGraphBuilder::GetFiltergraph(IGraphBuilder **ppGB)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("CDvdGraphBuilder::GetFiltergraph(0x%lx)"), ppGB)) ;
|
|
|
|
if (ppGB == NULL)
|
|
return E_POINTER ;
|
|
|
|
#if 0
|
|
EnsureGraphExists() ;
|
|
#endif // #if 0
|
|
|
|
*ppGB = m_pGB ;
|
|
if (NULL == m_pGB)
|
|
{
|
|
return E_UNEXPECTED ;
|
|
}
|
|
m_pGB->AddRef() ; // app owns a copy now
|
|
return NOERROR ;
|
|
}
|
|
|
|
|
|
#if 0 // NOT for now
|
|
//
|
|
// Set DVD source specification
|
|
//
|
|
HRESULT CDvdGraphBuilder::SetDvdSourceFilter(IBaseFilter *pDVDSrc, LPCWSTR lpszwSrcName)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("CDvdGraphBuilder::SetDvdSourceFilter(0x%lx, 0x%lx)"),
|
|
ppGB, lpszwSrcName)) ;
|
|
BOOL bGraphDone = m_bGraphDone ;
|
|
|
|
// if (m_pDVDSrc) // some DVD source filter was specified -- remove it
|
|
// m_pGB->RemoveFilter(m_pDVDSrc) ;
|
|
|
|
HRESULT hr ;
|
|
#if 0
|
|
hr = EnsureGraphExists() ;
|
|
if (FAILED(hr))
|
|
return hr ;
|
|
#endif
|
|
|
|
if (bGraphDone) // if graph was built before then rebuild it
|
|
{
|
|
ClearGraph() ;
|
|
// Also need to unload all the decoders currently loaded
|
|
// m_Decoders.CleanAll() ; -- should be covered by ClearGraph()
|
|
}
|
|
|
|
if (pDVDSrc) // a new DVD source filter is specified -- add it
|
|
m_pGB->AddFilter(pDVDSrc, lpszwSrcName) ;
|
|
|
|
m_pDVDSrc = pDVDSrc ; // this is the new DVD source (or NULL for default)
|
|
|
|
if (bGraphDone) // if graph was built then indicate rebuild needed
|
|
{
|
|
return VFW_S_DVD_NEEDREBUILD ;
|
|
}
|
|
|
|
// Otherwise we are done for now
|
|
|
|
return NOERROR ;
|
|
}
|
|
#endif // #if 0
|
|
|
|
|
|
//
|
|
// Get a specified interface off of a filter in the DVD playback graph
|
|
//
|
|
HRESULT CDvdGraphBuilder::GetDvdInterface(REFIID riid, void **ppvIF)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("CDvdGraphBuilder::GetDvdInterface(%s, 0x%lx)"),
|
|
(LPCSTR)CDisp(riid), ppvIF)) ;
|
|
|
|
if (IsBadWritePtr(ppvIF, sizeof(LPVOID)))
|
|
return E_INVALIDARG ;
|
|
*ppvIF = NULL ;
|
|
|
|
// We can't return any of the internal filter interface pointers before
|
|
// building the whole graph.
|
|
if (! m_bGraphDone )
|
|
return VFW_E_DVD_GRAPHNOTREADY ;
|
|
|
|
if (IID_IDvdControl == riid)
|
|
{
|
|
if (m_pDVDSrc) // if user specified DVD source
|
|
return m_pDVDSrc->QueryInterface(IID_IDvdControl, (LPVOID *)ppvIF) ;
|
|
else // if using default DVD source -- our Nav
|
|
return m_pDVDNav->QueryInterface(IID_IDvdControl, (LPVOID *)ppvIF) ;
|
|
}
|
|
else if (IID_IDvdInfo == riid)
|
|
{
|
|
if (m_pDVDSrc) // if user specified DVD source
|
|
return m_pDVDSrc->QueryInterface(IID_IDvdInfo, (LPVOID *)ppvIF) ;
|
|
else // if using default DVD source -- our Nav
|
|
return m_pDVDNav->QueryInterface(IID_IDvdInfo, (LPVOID *)ppvIF) ;
|
|
}
|
|
else if (IID_IVideoWindow == riid)
|
|
{
|
|
if (m_pVR)
|
|
return m_pGB->QueryInterface(IID_IVideoWindow, (LPVOID *)ppvIF) ;
|
|
else
|
|
return E_NOINTERFACE ;
|
|
}
|
|
else if (IID_IBasicVideo == riid)
|
|
{
|
|
if (m_pVR)
|
|
return m_pVR->QueryInterface(IID_IBasicVideo, (LPVOID *)ppvIF) ;
|
|
else
|
|
return E_NOINTERFACE ;
|
|
}
|
|
else if (IID_IBasicAudio == riid)
|
|
{
|
|
if (m_pAR)
|
|
return m_pAR->QueryInterface(IID_IBasicAudio, (LPVOID *)ppvIF) ;
|
|
else
|
|
return E_NOINTERFACE ;
|
|
}
|
|
else if (IID_IAMLine21Decoder == riid)
|
|
{
|
|
if (m_pL21Dec)
|
|
return m_pL21Dec->QueryInterface(IID_IAMLine21Decoder, (LPVOID *)ppvIF) ;
|
|
else
|
|
return E_NOINTERFACE ;
|
|
}
|
|
else
|
|
return E_NOINTERFACE ;
|
|
}
|
|
|
|
|
|
#define DVDGRAPH_FLAGSVALIDDEC 0x000000FF
|
|
|
|
#define DVDMG_HWDEC_NOTFOUND 0x01
|
|
#define DVDMG_SWDEC_NOTFOUND 0x02
|
|
|
|
//
|
|
// Build the whole graph for playing back the specifed or default DVD volume
|
|
//
|
|
HRESULT CDvdGraphBuilder::RenderDvdVideoVolume(LPCWSTR lpcwszPathName, DWORD dwFlags,
|
|
AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("CDvdGraphBuilder::RenderDvdVideoVolume(0x%lx, 0x%lx, 0x%lx)"),
|
|
lpcwszPathName, dwFlags, pStatus)) ;
|
|
|
|
HRESULT hr ;
|
|
|
|
#if 0
|
|
hr = EnsureGraphExists() ; // make sure that a graph exists; if not create one
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: Couldn't create a filter graph object"))) ;
|
|
return hr ;
|
|
}
|
|
#endif // #if 0
|
|
|
|
if (m_bGraphDone) // if graph was built before,
|
|
StopGraph() ; // just make sure the graph is in Stopped state first
|
|
|
|
ZeroMemory(pStatus, sizeof(AM_DVD_RENDERSTATUS)) ; // clear status
|
|
m_bUseVPE = (0 == (dwFlags & AM_DVD_NOVPE)) ; // is VPE needed?
|
|
|
|
if (0 == (dwFlags & DVDGRAPH_FLAGSVALIDDEC)) // 0 by default means HW max
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("dwFlags specified as 0x%lx; added .._HWDEC_PREFER"), dwFlags)) ;
|
|
dwFlags |= AM_DVD_HWDEC_PREFER ; // use HW Decs maxm
|
|
}
|
|
|
|
//
|
|
// Now build graph based on flag specified in the call
|
|
//
|
|
switch (dwFlags & DVDGRAPH_FLAGSVALIDDEC)
|
|
{
|
|
case AM_DVD_HWDEC_PREFER:
|
|
hr = MakeGraphHW(FALSE, pStatus) ;
|
|
break ;
|
|
|
|
case AM_DVD_HWDEC_ONLY:
|
|
hr = MakeGraphHW(TRUE, pStatus) ;
|
|
break ;
|
|
|
|
case AM_DVD_SWDEC_PREFER:
|
|
// m_bUseVPE = FALSE ;
|
|
hr = MakeGraphSW(FALSE, pStatus) ;
|
|
break ;
|
|
|
|
case AM_DVD_SWDEC_ONLY:
|
|
// m_bUseVPE = FALSE ;
|
|
hr = MakeGraphSW(TRUE, pStatus) ;
|
|
break ;
|
|
|
|
default:
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: Got an invalid DVD Render flag"))) ;
|
|
hr = E_INVALIDARG ;
|
|
break ;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("Graph building failed (Error 0x%lx)"), hr)) ;
|
|
// m_Decoders.CleanAll() ; -- should be covered by ClearGraph()
|
|
ClearGraph() ;
|
|
return hr ;
|
|
}
|
|
|
|
HRESULT hrFinal = hr ; // as it has been so far
|
|
|
|
//
|
|
// Now get all the mixers, renderers etc to complete the graph
|
|
//
|
|
hr = RenderDecoderOutput(pStatus) ;
|
|
|
|
//
|
|
// If we were successful before and the decoder output rendering wasn't
|
|
// then at least set the warning flag.
|
|
//
|
|
if (S_OK == hrFinal && S_OK != hr ||
|
|
SUCCEEDED(hrFinal) && FAILED(hr))
|
|
hrFinal = hr ;
|
|
|
|
//
|
|
// Do we really need it?
|
|
//
|
|
if (lpcwszPathName)
|
|
{
|
|
lstrcpynW(m_achwPathName, lpcwszPathName, MAX_PATH) ;
|
|
}
|
|
|
|
//
|
|
// Set the specified root file name/DVD volume name (even NULL because
|
|
// that causes the DVD Nav to search for one)
|
|
//
|
|
IDvdControl *pDvdC ;
|
|
if (m_pDVDSrc)
|
|
hr = m_pDVDSrc->QueryInterface(IID_IDvdControl, (LPVOID *)&pDvdC) ;
|
|
else
|
|
hr = m_pDVDNav->QueryInterface(IID_IDvdControl, (LPVOID *)&pDvdC) ;
|
|
if (FAILED(hr) || NULL == pDvdC)
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: Couldn't get IDvdControl interface (Error 0x%lx)"), hr)) ;
|
|
return hr ;
|
|
}
|
|
|
|
//
|
|
// Set the specified DVD volume path
|
|
//
|
|
// Does the SetRoor() function handle the NULL properly?
|
|
//
|
|
hr = pDvdC->SetRoot(lpcwszPathName) ;
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_TRACE, 2,
|
|
TEXT("IDvdControl::SetRoot() call couldn't use specified volume (Error 0x%lx)"), hr)) ;
|
|
if (lpcwszPathName)
|
|
pStatus->bDvdVolInvalid = TRUE ;
|
|
else
|
|
pStatus->bDvdVolUnknown = TRUE ;
|
|
if (SUCCEEDED(hrFinal)) // if we were so far perfect, ...
|
|
hrFinal = S_FALSE ; // ...we aren't so anymore
|
|
}
|
|
|
|
pDvdC->Release() ; // done with this interface
|
|
|
|
// Only if we haven't entirely failed, set the graph built flag and
|
|
// return overall result.
|
|
if (SUCCEEDED(hrFinal))
|
|
m_bGraphDone = TRUE ;
|
|
|
|
return hrFinal ;
|
|
}
|
|
|
|
|
|
#if 0
|
|
HRESULT CDvdGraphBuilder::GetStatusMessage(AM_DVD_RENDERSTATUS *pStatus,
|
|
LPTSTR lpszMessage, int *piLength)
|
|
{
|
|
return E_NOTIMPL ; // for now
|
|
}
|
|
#endif // #if 0
|
|
|
|
|
|
//
|
|
// Make sure a filter graph has been created; if not create one here
|
|
//
|
|
HRESULT CDvdGraphBuilder::EnsureGraphExists(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::EnsureGraphExists()"))) ;
|
|
|
|
if (m_pGB)
|
|
return S_OK ;
|
|
|
|
return CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
|
|
IID_IGraphBuilder, (LPVOID *)&m_pGB) ;
|
|
}
|
|
|
|
|
|
//
|
|
// Create a fresh filter graph
|
|
//
|
|
HRESULT CDvdGraphBuilder::CreateGraph(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::CreateGraph()"))) ;
|
|
|
|
return CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
|
|
IID_IGraphBuilder, (LPVOID *)&m_pGB) ;
|
|
}
|
|
|
|
|
|
//
|
|
// Delete the existing filter graph's contents
|
|
//
|
|
HRESULT CDvdGraphBuilder::DeleteGraph(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::DeleteGraph()"))) ;
|
|
|
|
m_pGB->Release() ;
|
|
return NOERROR ;
|
|
}
|
|
|
|
|
|
//
|
|
// Clear all the existing filters from the graph
|
|
//
|
|
HRESULT CDvdGraphBuilder::ClearGraph(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::ClearGraph()"))) ;
|
|
|
|
// Just paranoia...
|
|
if (NULL == m_pGB)
|
|
{
|
|
ASSERT(FALSE) ; // so that we know
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: How are we Clearing w/o a graph???"))) ;
|
|
return E_FAIL ;
|
|
}
|
|
|
|
// Reset state and release interfaces
|
|
m_dwVideoRenderStatus = 0 ;
|
|
if (m_pL21PinToRender)
|
|
{
|
|
m_pL21PinToRender->Release() ;
|
|
m_pL21PinToRender = NULL ;
|
|
}
|
|
if (m_pSPPinToRender)
|
|
{
|
|
m_pSPPinToRender->Release() ;
|
|
m_pSPPinToRender = NULL ;
|
|
}
|
|
|
|
// Remove all (connected only?) filters from graph
|
|
RemoveAllFilters() ;
|
|
|
|
// Release all decoders too
|
|
m_Decoders.CleanAll() ;
|
|
|
|
m_bGraphDone = FALSE ; // reset the "graph already built" flag
|
|
|
|
return NOERROR ;
|
|
}
|
|
|
|
|
|
//
|
|
// Delete all the non-decoder filters from the current graph.
|
|
//
|
|
HRESULT CDvdGraphBuilder::RemoveAllFilters(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RemoveAllFilters()"))) ;
|
|
|
|
if (m_pVR)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Removing Video Renderer from the graph"))) ;
|
|
m_pGB->RemoveFilter(m_pVR) ;
|
|
m_pVR->Release() ;
|
|
m_pVR = NULL ;
|
|
}
|
|
if (m_pAR)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Removing Audio Renderer from the graph"))) ;
|
|
m_pGB->RemoveFilter(m_pAR) ;
|
|
m_pAR->Release() ;
|
|
m_pAR = NULL ;
|
|
}
|
|
if (m_pVM)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Removing Video Mixer from the graph"))) ;
|
|
m_pGB->RemoveFilter(m_pVM) ;
|
|
m_pVM->Release() ;
|
|
m_pVM = NULL ;
|
|
}
|
|
if (m_pL21Dec)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Removing Line21 dec from the graph"))) ;
|
|
m_pGB->RemoveFilter(m_pL21Dec) ;
|
|
m_pL21Dec->Release() ;
|
|
m_pL21Dec = NULL ;
|
|
}
|
|
|
|
m_IntFilters.RemoveAll() ; // remove intermediate filters, if any
|
|
|
|
// Now remove the Nav
|
|
if (m_pDVDNav)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Removing DVD Nav from the graph"))) ;
|
|
m_pGB->RemoveFilter(m_pDVDNav) ;
|
|
m_pDVDNav->Release() ;
|
|
m_pDVDNav = NULL ;
|
|
}
|
|
|
|
return NOERROR ;
|
|
}
|
|
|
|
|
|
//
|
|
// Create a filter and add it to the filter graph
|
|
//
|
|
HRESULT CDvdGraphBuilder::CreateFilterInGraph(CLSID Clsid,
|
|
LPCTSTR lpszFilterName, IBaseFilter **ppFilter)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::CreateFilterInGraph(%s, %s, 0x%lx)"),
|
|
(LPCSTR) CDisp(Clsid), lpszFilterName, ppFilter)) ;
|
|
|
|
if (NULL == m_pGB)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Filter graph object hasn't been created yet"))) ;
|
|
return E_FAIL ;
|
|
}
|
|
|
|
HRESULT hr ;
|
|
hr = CoCreateInstance(Clsid, NULL, CLSCTX_INPROC, IID_IBaseFilter,
|
|
(LPVOID *)ppFilter) ;
|
|
if (FAILED(hr) || NULL == *ppFilter)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Couldn't create filter %s (Error 0x%lx)"),
|
|
(LPCTSTR)CDisp(Clsid), hr)) ;
|
|
return hr ;
|
|
}
|
|
|
|
// Add it to the filter graph
|
|
WCHAR achwFilterName[MAX_FILTER_NAME] ;
|
|
#ifdef UNICODE
|
|
lstrcpy(achwFilterName, lpszFilterName) ;
|
|
#else
|
|
MultiByteToWideChar(CP_ACP, 0, lpszFilterName, -1, achwFilterName, MAX_FILTER_NAME) ;
|
|
#endif // UNICODE
|
|
hr = m_pGB->AddFilter(*ppFilter, achwFilterName) ;
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Couldn't add filter %s to graph (Error 0x%lx)"),
|
|
(LPCTSTR)CDisp(Clsid), hr)) ;
|
|
(*ppFilter)->Release() ; // release filter too
|
|
*ppFilter = NULL ; // and set it to NULL
|
|
return hr ;
|
|
}
|
|
|
|
return NOERROR ;
|
|
}
|
|
|
|
|
|
//
|
|
// Find the nth pin with a specific direction in a filter
|
|
//
|
|
HRESULT CDvdGraphBuilder::FindOpenPin(IBaseFilter *pFilter, PIN_DIRECTION pd,
|
|
int iIndex, IPin **ppPin)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::FindOpenPin(0x%lx, %d, %d, 0x%lx)"),
|
|
pFilter, pd, iIndex, ppPin)) ;
|
|
|
|
HRESULT hr ;
|
|
IEnumPins *pEnumPins ;
|
|
IPin *pPin ;
|
|
IPin *pPin2 ;
|
|
PIN_DIRECTION pdFound ;
|
|
ULONG ul ;
|
|
|
|
*ppPin = NULL ;
|
|
|
|
if (NULL == pFilter)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Can't find a pin from NULL filter!!!"))) ;
|
|
return E_INVALIDARG ;
|
|
}
|
|
|
|
hr = pFilter->EnumPins(&pEnumPins) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumPins) ;
|
|
|
|
while (S_OK == pEnumPins->Next(1, &pPin, &ul) && 1 == ul)
|
|
{
|
|
hr = pPin->QueryDirection(&pdFound) ;
|
|
ASSERT(SUCCEEDED(hr)) ;
|
|
if (pd != pdFound)
|
|
{
|
|
pPin->Release() ; // don't need this pin
|
|
continue ;
|
|
}
|
|
hr = pPin->ConnectedTo(&pPin2) ;
|
|
if (SUCCEEDED(hr) && pPin2)
|
|
{
|
|
pPin2->Release() ; // we don't want this pin actually
|
|
pPin->Release() ; // this pin is already connected
|
|
continue ; // try next one
|
|
}
|
|
if (0 == iIndex)
|
|
{
|
|
// Got the reqd pin in the right direction
|
|
*ppPin = pPin ;
|
|
hr = S_OK ;
|
|
break ;
|
|
}
|
|
else // some more to go
|
|
{
|
|
iIndex-- ; // one more down...
|
|
pPin->Release() ; // this is not the pin we are looking for
|
|
}
|
|
}
|
|
pEnumPins->Release() ;
|
|
return hr ; // whatever it is
|
|
}
|
|
|
|
|
|
BOOL CDvdGraphBuilder::CheckPinMediaTypeMatch(IPin *pPinIn, DWORD dwStreamFlag)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::CheckPinMediaTypeMatch(%s, %lu)"),
|
|
(LPCTSTR) CDisp(pPinIn), dwStreamFlag)) ;
|
|
|
|
BOOL bResult = FALSE ;
|
|
AM_MEDIA_TYPE *pmtIn ;
|
|
IEnumMediaTypes *pEnumMTIn ;
|
|
HRESULT hr = pPinIn->EnumMediaTypes(&pEnumMTIn) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumMTIn) ;
|
|
ULONG ul ;
|
|
while (!bResult &&
|
|
S_OK == pEnumMTIn->Next(1, &pmtIn, &ul) && 1 == ul) // more mediatypes
|
|
{
|
|
// Decipher the mediatype
|
|
if (pmtIn->majortype == MEDIATYPE_MPEG2_PES ||
|
|
pmtIn->majortype == MEDIATYPE_DVD_ENCRYPTED_PACK)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is MPEG2_PES/DVD_ENCRYPTED_PACK"))) ;
|
|
|
|
if (pmtIn->subtype == MEDIASUBTYPE_MPEG2_VIDEO)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Subtype is MPEG2_VIDEO"))) ;
|
|
bResult = dwStreamFlag == AM_DVD_STREAM_VIDEO ;
|
|
}
|
|
else if (pmtIn->subtype == MEDIASUBTYPE_DOLBY_AC3)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Subtype is DOLBY_AC3"))) ;
|
|
bResult = dwStreamFlag == AM_DVD_STREAM_AUDIO ;
|
|
}
|
|
else if (pmtIn->subtype == MEDIASUBTYPE_DVD_SUBPICTURE)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Subtype is DVD_Subpicture"))) ;
|
|
bResult = dwStreamFlag == AM_DVD_STREAM_SUBPIC ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Unknown subtype %s"),
|
|
(LPCSTR) CDisp(pmtIn->subtype))) ;
|
|
}
|
|
}
|
|
else if (pmtIn->majortype == MEDIATYPE_Video) // elementary stream
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is Video elementary"))) ;
|
|
|
|
if (pmtIn->subtype == MEDIASUBTYPE_DVD_SUBPICTURE)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Subtype is DVD_SUBPICTURE"))) ;
|
|
bResult = dwStreamFlag == AM_DVD_STREAM_SUBPIC ;
|
|
}
|
|
else if (pmtIn->subtype == MEDIASUBTYPE_MPEG2_VIDEO)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Subtype is MPEG2_VIDEO"))) ;
|
|
bResult = dwStreamFlag == AM_DVD_STREAM_VIDEO ;
|
|
}
|
|
else
|
|
DbgLog((LOG_TRACE, 5, TEXT("WARNING: Unknown subtype %s"),
|
|
(LPCSTR) CDisp(pmtIn->subtype))) ;
|
|
}
|
|
else if (pmtIn->majortype == MEDIATYPE_Audio) // elementary stream
|
|
{
|
|
ASSERT(pmtIn->subtype == MEDIASUBTYPE_DOLBY_AC3) ; // just checking
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is Audio elementary"))) ;
|
|
bResult = dwStreamFlag == AM_DVD_STREAM_AUDIO ;
|
|
}
|
|
//
|
|
// There is a chance that some IHV/ISV creates a private mediatype
|
|
// (major or sub) as in the case of IBM (for CSS filter). We have to
|
|
// search the parts of the mediatype to locate something we recognize.
|
|
//
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 2,
|
|
TEXT("Unknown mediatype %s:%s. But we won't give up..."),
|
|
(LPCSTR) CDisp(pmtIn->majortype), (LPCSTR) CDisp(pmtIn->subtype))) ;
|
|
if (pmtIn->subtype == MEDIASUBTYPE_DOLBY_AC3)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is ISV/IHV-specific Audio"))) ;
|
|
bResult = dwStreamFlag == AM_DVD_STREAM_AUDIO ;
|
|
}
|
|
else if (pmtIn->subtype == MEDIASUBTYPE_MPEG2_VIDEO)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is ISV/IHV-specific Video"))) ;
|
|
bResult = dwStreamFlag == AM_DVD_STREAM_VIDEO ;
|
|
}
|
|
else if (pmtIn->subtype == MEDIASUBTYPE_DVD_SUBPICTURE)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is ISV/IHV-specific Subpicture"))) ;
|
|
bResult = dwStreamFlag == AM_DVD_STREAM_SUBPIC ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 2, TEXT("WARNING: Unknown mediatype. Couldn't detect at all."))) ;
|
|
}
|
|
}
|
|
|
|
DeleteMediaType(pmtIn) ;
|
|
} // end of while()
|
|
|
|
pEnumMTIn->Release() ;
|
|
|
|
return bResult ; // whatever we found
|
|
|
|
}
|
|
|
|
|
|
IBaseFilter * CDvdGraphBuilder::GetFilterBetweenPins(IPin *pPinOut, IPin *pPinIn)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::GetFilterBetweenPins(Out=%s, In=%s)"),
|
|
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
|
|
|
|
if (NULL == pPinOut || NULL == pPinIn) // what!!!
|
|
return NULL ;
|
|
|
|
IPin *pPin ;
|
|
PIN_INFO pi ;
|
|
IBaseFilter *pFilter = NULL ;
|
|
HRESULT hr = pPinOut->ConnectedTo(&pPin) ;
|
|
if (pPin && SUCCEEDED(hr))
|
|
{
|
|
pPin->QueryPinInfo(&pi) ;
|
|
pFilter = pi.pFilter ;
|
|
ASSERT(pFilter && PINDIR_INPUT == pi.dir) ;
|
|
//
|
|
// We intentionally keep the extra ref count because this is an intermediate
|
|
// filter and other intermediate filters picked up based on registry based
|
|
// filter enum (for SW decoding case) will have the extra ref count. We
|
|
// release the IBaseFilter interface pointer in CIntFilters::RemoveAll() and
|
|
// if we don't keep this extra ref count here, we'll fault. On the other
|
|
// hand we must do Release() on CIntFilters elements, because SW enum-ed
|
|
// filters will not be unloaded.
|
|
//
|
|
// if (pi.pFilter)
|
|
// pi.pFilter->Release() ; // it has an extra ref count from the Query...
|
|
|
|
pPin->Release() ; // done with the pin for now
|
|
|
|
// Just for checking...
|
|
#ifdef DEBUG
|
|
hr = pPinIn->ConnectedTo(&pPin) ;
|
|
if (pPin && SUCCEEDED(hr))
|
|
{
|
|
pPin->QueryPinInfo(&pi) ;
|
|
ASSERT(pi.pFilter && PINDIR_OUTPUT == pi.dir) ;
|
|
if (pi.pFilter)
|
|
{
|
|
ASSERT(IsEqualObject(pFilter, pi.pFilter)) ; // we should have got the same filter
|
|
pi.pFilter->Release() ; // it has an extra ref count from the Query...
|
|
}
|
|
|
|
pPin->Release() ; // done with the pin for now
|
|
}
|
|
#endif // DEBUG
|
|
|
|
}
|
|
return pFilter ;
|
|
}
|
|
|
|
|
|
HRESULT CDvdGraphBuilder::ConnectSrcToHWDec(IBaseFilter *pSrc,
|
|
CListDecoders *pHWDecList,
|
|
AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::ConnectSrcToHWDec(0x%lx, 0x%0x, 0x%lx)"),
|
|
pSrc, pHWDecList, pStatus)) ;
|
|
|
|
ULONG ul ;
|
|
int i ;
|
|
IPin *pPinOut ;
|
|
IPin *pPinIn ;
|
|
PIN_DIRECTION pd ;
|
|
BOOL bConnected ;
|
|
IBaseFilter *pHWDec ;
|
|
LPTSTR szName ;
|
|
BOOL bHW ;
|
|
IEnumPins *pEnumPinsOut ;
|
|
HRESULT hr ;
|
|
HRESULT hrFinal = S_OK ; // assumed innocent
|
|
|
|
if (0 == pHWDecList->GetNumHWFilters()) // if there is no HW decoder
|
|
return S_FALSE ; // not a failure, but not a full success
|
|
|
|
if (NULL == pSrc)
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: NULL Src passed to ConnectSrcToHWDec()"))) ;
|
|
return E_INVALIDARG ;
|
|
}
|
|
|
|
hr = pSrc->EnumPins(&pEnumPinsOut) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumPinsOut) ;
|
|
|
|
while (S_OK == pEnumPinsOut->Next(1, &pPinOut, &ul) && 1 == ul)
|
|
{
|
|
pPinOut->QueryDirection(&pd) ;
|
|
if (PINDIR_INPUT == pd)
|
|
{
|
|
pPinOut->Release() ;
|
|
continue ;
|
|
}
|
|
hr = pPinOut->ConnectedTo(&pPinIn) ;
|
|
if (SUCCEEDED(hr) && pPinIn)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Pin %s is already connected to %s"),
|
|
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
|
|
pPinIn->Release() ; // we don't want this pin actually
|
|
pPinOut->Release() ; // this pin is already connected
|
|
continue ; // try next one
|
|
}
|
|
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Pin %s will be tried for connection to HW decoder"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
|
|
// Got an unconnected output pin of the DVD source filter
|
|
//
|
|
// We are going to get the successive inpins of the already
|
|
// loaded decoders and see if the outpin can connect directly
|
|
// to it.
|
|
bConnected = FALSE ;
|
|
|
|
// Try all HW decoders for mediatype match and then connection
|
|
DbgLog((LOG_TRACE, 5, TEXT("Going to try %d HW decoders..."),
|
|
pHWDecList->GetNumHWFilters())) ;
|
|
for (i = 0 ; !bConnected && i < pHWDecList->GetNumHWFilters() ; i++)
|
|
{
|
|
pHWDecList->GetFilter(i, &pHWDec, &szName, &bHW) ;
|
|
ASSERT(pHWDec && bHW) ; // paranoia!!!
|
|
DbgLog((LOG_TRACE, 5, TEXT("HW Decoder: %s"), szName)) ;
|
|
|
|
BOOL bNotInUse ;
|
|
bNotInUse = (DECLIST_NOTFOUND == m_Decoders.IsInList(TRUE, (LPVOID)pHWDec)) ;
|
|
if (bNotInUse) // not already in the graph/decoder list
|
|
{
|
|
// First add filter to graph and then try to connect
|
|
WCHAR achwFilterName[MAX_FILTER_NAME] ;
|
|
#ifdef UNICODE
|
|
lstrcpy(achwFilterName, szName) ;
|
|
#else
|
|
MultiByteToWideChar(CP_ACP, 0, szName, -1, achwFilterName,
|
|
MAX_FILTER_NAME) ;
|
|
#endif // UNICODE
|
|
|
|
m_pGB->AddFilter(pHWDec, achwFilterName) ;
|
|
}
|
|
else
|
|
DbgLog((LOG_TRACE, 4, TEXT("HW decoder (%s) is already in the graph"), szName)) ;
|
|
|
|
//
|
|
// Try all input pins of this HW decoder until we can connect the
|
|
// Nav out pin to one (or all in pins have been tried)
|
|
//
|
|
int j = 0 ;
|
|
while ( !bConnected &&
|
|
SUCCEEDED(hr = FindOpenPin(pHWDec, PINDIR_INPUT, j, &pPinIn)) &&
|
|
pPinIn)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Open input pin #%d found on HW decoder"), j)) ;
|
|
|
|
//
|
|
// Find the mediatype of this input pin of the proxy filter. If
|
|
// it's the same type as the output pin of the Nav, then they
|
|
// are supposed to be connected (may be) -- try it.
|
|
//
|
|
DWORD dwStreamIn = StreamFlagFromSWPin(pPinOut) ;
|
|
if (CheckPinMediaTypeMatch(pPinIn, dwStreamIn))
|
|
{
|
|
DbgLog((LOG_TRACE, 3,
|
|
TEXT("Pin <%s> and Pin <%s> has matching mediatypes (%lu)"),
|
|
(LPCTSTR) CDisp(pPinOut), (LPCTSTR) CDisp(pPinIn), dwStreamIn)) ;
|
|
|
|
//
|
|
// First try to do "direct connect" so that no other intermediate
|
|
// filter gets picked up (which is the most common case).
|
|
//
|
|
hr = m_pGB->ConnectDirect(pPinOut, pPinIn, NULL) ;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
bConnected = TRUE ;
|
|
// Add it to list, ONLY IF not already in
|
|
if (bNotInUse)
|
|
m_Decoders.AddFilter(pHWDec, szName, TRUE, NULL) ;
|
|
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Pin %s directly connected to pin %s"),
|
|
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
|
|
}
|
|
else // direct connection doesn't work
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Pin %s did NOT directly connect to pin %s"),
|
|
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
|
|
|
|
//
|
|
// Now do an "intelligent connect" so that any required intermediate
|
|
// filter can get picked up. This is mainly to accommodate IBM's
|
|
// separate CSS filter.
|
|
//
|
|
hr = m_pGB->Connect(pPinOut, pPinIn) ;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
bConnected = TRUE ;
|
|
// Add it to list, ONLY IF not already in
|
|
if (bNotInUse)
|
|
m_Decoders.AddFilter(pHWDec, szName, TRUE, NULL) ;
|
|
|
|
//
|
|
// BUGBUG: We assume that there will be only one intermediate
|
|
// filter sitting between the Nav's out pin and the decoder,
|
|
// which is the most likely case, but there IS a chance that
|
|
// there can be two more such filters. We should rather add
|
|
// all such filters to the intermediate filters' list inside
|
|
// GetFilter(s)BetweenPins() method.
|
|
//
|
|
m_IntFilters.AddFilter(GetFilterBetweenPins(pPinOut, pPinIn)) ; // add to the list
|
|
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Pin %s *indirectly* connected to pin %s"),
|
|
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
|
|
}
|
|
else
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Pin %s did NOT indirectly connect to pin %s"),
|
|
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
|
|
} // end of else of if (connect direct)
|
|
} // end of if (CheckPinMediaTypeMatch())
|
|
|
|
pPinIn->Release() ; // done with this in pin
|
|
j++ ;
|
|
} // end of while (try all in pins untill connected to one)
|
|
|
|
if (0 == j)
|
|
DbgLog((LOG_TRACE, 5, TEXT("No open input pin found on HW decoder"))) ;
|
|
|
|
// If we didn't connect to it AND we added it to the graph
|
|
// THIS TIME then ONLY remove it. I spent almost day debugging
|
|
// for the missing if (bNotInUse) check!!!
|
|
if ( !bConnected && bNotInUse)
|
|
m_pGB->RemoveFilter(pHWDec) ;
|
|
|
|
} // end of for (all HW decs)
|
|
|
|
if (! bConnected ) // if we couldn't connect this pin
|
|
{
|
|
if (NOERROR == hrFinal) // if it was perfect so far,
|
|
hrFinal = S_FALSE ; // it's not so anymore
|
|
DbgLog((LOG_TRACE, 5, TEXT("Pin (%s) couldn't be connected to any HW Dec"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
}
|
|
pPinOut->Release() ; // done with this out pin
|
|
|
|
} // end of while (out pin enum)
|
|
pEnumPinsOut->Release() ; // done with pin enum of DVD Src
|
|
|
|
return hrFinal ;
|
|
}
|
|
|
|
|
|
BOOL CDvdGraphBuilder::StartDecAndConnect(IPin *pPinOut,
|
|
IFilterMapper *pMapper,
|
|
AM_MEDIA_TYPE *pmt)
|
|
{
|
|
ULONG ul ;
|
|
IPin *pPinIn ;
|
|
PIN_DIRECTION pd ;
|
|
BOOL bConnected ;
|
|
LPTSTR szName ;
|
|
IEnumPins *pEnumPinsOut ;
|
|
IEnumRegFilters *pEnumFilters ;
|
|
IBaseFilter *pSWDec ;
|
|
REGFILTER *pRegDec ;
|
|
TCHAR achName[MAX_FILTER_NAME] ;
|
|
IEnumMediaTypes *pEnumMT ;
|
|
HRESULT hr ;
|
|
HRESULT hrFinal = S_OK ; // assumed innocent
|
|
BOOL bHW ;
|
|
int iDecPos ;
|
|
|
|
hr = pMapper->EnumMatchingFilters(&pEnumFilters, /* MERIT_NORMAL */
|
|
MERIT_DO_NOT_USE+1, TRUE,
|
|
pmt->majortype, pmt->subtype, FALSE, TRUE,
|
|
GUID_NULL, GUID_NULL) ;
|
|
if (FAILED(hr) || NULL == pEnumFilters)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("No matching enum for mediatype (%s:%s)"),
|
|
(LPCSTR) CDisp(pmt->majortype), (LPCSTR) CDisp(pmt->subtype))) ;
|
|
DeleteMediaType(pmt) ;
|
|
return FALSE ;
|
|
}
|
|
DbgLog((LOG_TRACE, 5, TEXT("Got a filter enum for mediatype (%s:%s)"),
|
|
(LPCSTR) CDisp(pmt->majortype), (LPCSTR) CDisp(pmt->subtype))) ;
|
|
|
|
// Go through all the filters in this enumerator to locate a
|
|
// suitable one to connect to.
|
|
bConnected = FALSE ; // clear flag at starting
|
|
while ( !bConnected &&
|
|
S_OK == pEnumFilters->Next(1, &pRegDec, &ul) && 1 == ul )
|
|
{
|
|
iDecPos = m_Decoders.IsInList(FALSE, (LPVOID)&(pRegDec->Clsid)) ;
|
|
if (DECLIST_NOTFOUND == iDecPos) // not in list
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Not an existing SW decoder-- going to create"))) ;
|
|
hr = CoCreateInstance(pRegDec->Clsid, NULL, CLSCTX_INPROC,
|
|
IID_IBaseFilter, (LPVOID *)&pSWDec) ;
|
|
if (FAILED(hr) || NULL == pSWDec)
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("Couldn't load a matching registered filter (%s) (Error 0x%lx)"),
|
|
(LPCSTR) CDisp(pRegDec->Clsid), hr)) ;
|
|
CoTaskMemFree(pRegDec) ; // before we try the next
|
|
continue ; // onto the next matching filter...
|
|
}
|
|
DbgLog((LOG_TRACE, 5, TEXT("Started matching registered filter (%s)"),
|
|
(LPCSTR) CDisp(pRegDec->Clsid))) ;
|
|
|
|
hr = m_pGB->AddFilter(pSWDec, pRegDec->Name) ;
|
|
ASSERT(SUCCEEDED(hr)) ;
|
|
}
|
|
else // already created; use existing instance
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Going to use an existing SW decoder (%d)"),
|
|
iDecPos)) ;
|
|
m_Decoders.GetFilter(iDecPos, &pSWDec, &szName, &bHW) ;
|
|
ASSERT(pSWDec && !bHW) ; // want to be sure!!
|
|
}
|
|
|
|
if (SUCCEEDED(hr = TryConnect(pPinOut, pSWDec, NULL, TRUE)))
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("One more pin connected (to decoder or 3rd party filter?)"))) ;
|
|
|
|
//
|
|
// There is a chance that the filter we just above created and connected
|
|
// Nav's out pin to, "may NOT be" actually a decoder -- it may also be a
|
|
// 3rd party intermediate filter, like IBM's CSS filter.
|
|
//
|
|
IPin *pPinOut2 ;
|
|
IEnumMediaTypes *pEnumMT2 ;
|
|
AM_MEDIA_TYPE *pmt2 ;
|
|
BOOL bMTDecoded = FALSE ;
|
|
hr = FindOpenPin(pSWDec, PINDIR_OUTPUT, 0, &pPinOut2) ;
|
|
if (SUCCEEDED(hr) && pPinOut2)
|
|
{
|
|
hr = pPinOut2->EnumMediaTypes(&pEnumMT2) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumMT2) ;
|
|
while ( !bMTDecoded &&
|
|
S_OK == pEnumMT2->Next(1, &pmt2, &ul) && 1 == ul)
|
|
{
|
|
bMTDecoded = MEDIATYPE_Video == pmt2->majortype ||
|
|
MEDIATYPE_Audio == pmt2->majortype ;
|
|
DeleteMediaType(pmt2) ; // otherwise
|
|
}
|
|
if (bMTDecoded) // we HAVE connected to a decoder!!!
|
|
{
|
|
//
|
|
// Add decoder info to our decoder list and set the success flag
|
|
//
|
|
DbgLog((LOG_TRACE, 5, TEXT("Nav pin / 3rd party's pin connected to decoder"))) ;
|
|
#ifdef UNICODE
|
|
lstrcpy(achName, pRegDec->Name) ;
|
|
#else
|
|
WideCharToMultiByte(CP_ACP, 0, pRegDec->Name, -1,
|
|
achName, MAX_FILTER_NAME, NULL, NULL) ;
|
|
#endif // UNICODE
|
|
|
|
if (DECLIST_NOTFOUND == iDecPos) // only if not in list
|
|
m_Decoders.AddFilter(pSWDec, achName, FALSE, &(pRegDec->Clsid)) ;
|
|
bConnected = TRUE ; // success!!
|
|
}
|
|
else // we have connected to an intermediate filter so far
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Nav pin (%s) connected to 3rd party filter with out pin (%s)"),
|
|
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinOut2))) ;
|
|
|
|
m_IntFilters.AddFilter(pSWDec) ; // pSWDec is actually the intermediate filter
|
|
|
|
pEnumMT2->Reset() ; // need to enum mediatypes AGAIN for connection
|
|
while( !bConnected &&
|
|
S_OK == pEnumMT2->Next(1, &pmt2, &ul) && 1 == ul )
|
|
{
|
|
bConnected = StartDecAndConnect(pPinOut2, pMapper, pmt2) ;
|
|
DeleteMediaType(pmt2) ; // done with this mediatype
|
|
} // end of while (!bConnected && pEnumMT2->Next())
|
|
|
|
if ( !bConnected ) // connection attempts failed
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Pin (%s) -> Pin(%s) couldn't be connected to any SW Dec"),
|
|
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinOut2))) ;
|
|
}
|
|
pEnumMT2->Release() ; // media enum over
|
|
}
|
|
pPinOut2->Release() ; // done with this pin
|
|
|
|
}
|
|
else // connection attempt failed
|
|
{
|
|
if (DECLIST_NOTFOUND == iDecPos) // only if we created it in this pass
|
|
{
|
|
hr = m_pGB->RemoveFilter(pSWDec) ; // connection failed; remove it
|
|
ASSERT(SUCCEEDED(hr)) ;
|
|
pSWDec->Release() ; // don't need it
|
|
}
|
|
}
|
|
CoTaskMemFree(pRegDec) ; // before we forget
|
|
|
|
} // end of while (!bConnected && pEnumFilters->Next())
|
|
|
|
pEnumFilters->Release() ; // let go of the filter enumerator
|
|
|
|
return bConnected ;
|
|
}
|
|
|
|
|
|
HRESULT CDvdGraphBuilder::ConnectSrcToSWDec(IBaseFilter *pSrc,
|
|
AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::ConnectSrcToSWDec(0x%lx, 0x%lx)"),
|
|
pSrc, pStatus)) ;
|
|
|
|
ULONG ul ;
|
|
IPin *pPinOut ;
|
|
IPin *pPinIn ;
|
|
PIN_DIRECTION pd ;
|
|
BOOL bConnected ;
|
|
IEnumPins *pEnumPinsOut ;
|
|
AM_MEDIA_TYPE *pmt ;
|
|
IEnumMediaTypes *pEnumMT ;
|
|
IFilterMapper *pMapper ;
|
|
HRESULT hr ;
|
|
HRESULT hrFinal = S_OK ; // assumed innocent
|
|
|
|
if (NULL == pSrc)
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: NULL Src passed to ConnectSrcToSWDec()"))) ;
|
|
return E_INVALIDARG ;
|
|
}
|
|
|
|
hr = CoCreateInstance(CLSID_FilterMapper, NULL, CLSCTX_INPROC,
|
|
IID_IFilterMapper, (LPVOID *)&pMapper) ;
|
|
ASSERT(SUCCEEDED(hr) && pMapper) ;
|
|
|
|
hr = pSrc->EnumPins(&pEnumPinsOut) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumPinsOut) ;
|
|
|
|
while (S_OK == pEnumPinsOut->Next(1, &pPinOut, &ul) && 1 == ul)
|
|
{
|
|
pPinOut->QueryDirection(&pd) ;
|
|
if (PINDIR_INPUT == pd)
|
|
{
|
|
pPinOut->Release() ;
|
|
continue ;
|
|
}
|
|
hr = pPinOut->ConnectedTo(&pPinIn) ;
|
|
if (SUCCEEDED(hr) && pPinIn) // already connected out pin of DVD src
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Pin %s is already connected to pin %s"),
|
|
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
|
|
pPinIn->Release() ; // let the in pin go
|
|
pPinOut->Release() ; // let the out pin go
|
|
continue ;
|
|
}
|
|
|
|
// Got an unconnected out pin of DVD src
|
|
DbgLog((LOG_TRACE, 5, TEXT("Found pin %s not yet connected"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
|
|
// First check if there is any existing intermediate filter(s).
|
|
// If so, we may be able to connect through it.
|
|
int iCount = m_IntFilters.GetCount() ;
|
|
DbgLog((LOG_TRACE, 5, TEXT("%d intermediate filters in list"), iCount)) ;
|
|
IBaseFilter *pIntFilter ;
|
|
for (int i = 0 ; i < iCount ; i++)
|
|
{
|
|
pIntFilter = m_IntFilters.GetFilter(i) ;
|
|
ASSERT(pIntFilter) ;
|
|
if (SUCCEEDED(hr = TryConnect(pPinOut, pIntFilter, NULL, TRUE)))
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Nav's out pin (%s) connected to 3rd party filter"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
pPinOut->Release() ; // done with Nav's pin; now we need int. filter's pin
|
|
// Next we'll get the out pin of the intermediate filter and release it
|
|
// at the end of the while loop, in place of the above pPinOut.
|
|
|
|
hr = FindOpenPin(pIntFilter, PINDIR_OUTPUT, 0, &pPinOut) ;
|
|
if (SUCCEEDED(hr) && pPinOut)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Got 3rd party filter's out pin %s"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
break ; // done for this pin of Nav; next step below.
|
|
}
|
|
}
|
|
else
|
|
DbgLog((LOG_TRACE, 5, TEXT("Nav's pin %s didn't connect to int. filter #%d"),
|
|
(LPCTSTR)CDisp(pPinOut), i)) ;
|
|
}
|
|
|
|
// Now we try to connect to the decoder (or an intermediate filter)
|
|
bConnected = FALSE ;
|
|
hr = pPinOut->EnumMediaTypes(&pEnumMT) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumMT) ;
|
|
while( !bConnected &&
|
|
S_OK == pEnumMT->Next(1, &pmt, &ul) && 1 == ul )
|
|
{
|
|
bConnected = StartDecAndConnect(pPinOut, pMapper, pmt) ;
|
|
DeleteMediaType(pmt) ; // done with this mediatype
|
|
|
|
} // end of while (!bConnected && pEnumMT->Next())
|
|
|
|
pEnumMT->Release() ; // done with mediatype enum
|
|
|
|
if (!bConnected) { // connection attempts failed
|
|
if (NOERROR == hrFinal) // if it was perfect so far,
|
|
hrFinal = S_FALSE ; // it's not so anymore
|
|
DbgLog((LOG_TRACE, 5, TEXT("Pin (%s) couldn't be connected to any SW Dec"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
}
|
|
pPinOut->Release() ; // done with this out pin
|
|
|
|
} // end of while (pEnumPinsOut->Next())
|
|
|
|
pEnumPinsOut->Release() ; // done with DVD source's pin enum
|
|
pMapper->Release() ; // done with the filter mapper
|
|
|
|
return hrFinal ;
|
|
}
|
|
|
|
|
|
HRESULT CDvdGraphBuilder::CheckSrcPinConnection(IBaseFilter *pSrc,
|
|
AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::CheckSrcPinConnection(0x%lx, 0x%lx)"),
|
|
pSrc, pStatus)) ;
|
|
|
|
ULONG ul ;
|
|
IPin *pPinOut ;
|
|
IPin *pPinIn ;
|
|
PIN_DIRECTION pd ;
|
|
IEnumPins *pEnumSrcPins ;
|
|
DWORD dwStreamOut ;
|
|
HRESULT hrFinal = S_OK ;
|
|
|
|
HRESULT hr = pSrc->EnumPins(&pEnumSrcPins) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumSrcPins) ;
|
|
|
|
while (S_OK == pEnumSrcPins->Next(1, &pPinOut, &ul) && 1 == ul)
|
|
{
|
|
pPinOut->QueryDirection(&pd) ;
|
|
if (PINDIR_INPUT == pd)
|
|
{
|
|
// Huh!!! In pin for DVD Src? Anyway...
|
|
pPinOut->Release() ;
|
|
continue ;
|
|
}
|
|
dwStreamOut = StreamFlagFromSWPin(pPinOut) ;
|
|
pStatus->iNumStreams++ ; // one more DVD stream found
|
|
pPinIn = NULL ; // start as NULL
|
|
|
|
if (m_IntFilters.GetCount() > 0) // there is indirect connection
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Nav pin %s connection will be checked aginst %d int. filters"),
|
|
(LPCTSTR)CDisp(pPinOut), m_IntFilters.GetCount())) ;
|
|
hr = pPinOut->ConnectedTo(&pPinIn) ; // get connected filter's in pin
|
|
if (SUCCEEDED(hr) && pPinIn)
|
|
{
|
|
PIN_INFO pi ;
|
|
pPinIn->QueryPinInfo(&pi) ;
|
|
// pPinIn->Release() ; // got the intermediate filter ptr, let go of the pin
|
|
|
|
if (m_IntFilters.IsInList(pi.pFilter)) // Nav pin is going via one of the intermediate filters
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Nav pin %s is connected to an int. filter in our list"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
|
|
pPinIn->Release() ; // got the intermediate filter ptr, let go of the pin
|
|
pPinIn = NULL ; // reset the pointer too
|
|
|
|
// Locate the "corresponding" out pin of the intermediate filter.
|
|
// Use the Nav out pin's mediatype to detect the correspodning out
|
|
// pin of the intermediate filter.
|
|
IPin *pIntFPinOut ;
|
|
IEnumPins *pEnumIntFPins ;
|
|
hr = pi.pFilter->EnumPins(&pEnumIntFPins) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumIntFPins) ;
|
|
while (NULL == pPinIn &&
|
|
S_OK == pEnumIntFPins->Next(1, &pIntFPinOut, &ul) && 1 == ul)
|
|
{
|
|
pIntFPinOut->QueryDirection(&pd) ;
|
|
if (PINDIR_OUTPUT == pd) // we only care about out pins
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Int. filter's pin %s mediatype is being determined..."),
|
|
(LPCTSTR)CDisp(pIntFPinOut))) ; // pPinOut
|
|
DWORD dwStream = StreamFlagFromSWPin(pIntFPinOut) ;
|
|
if (dwStream == dwStreamOut) // corresponding pin
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Found out pin of int. filter corresponding to %s of Nav (Stream 0x%lx)"),
|
|
(LPCTSTR)CDisp(pPinOut), dwStreamOut)) ;
|
|
hr = pIntFPinOut->ConnectedTo(&pPinIn) ; // get connection info
|
|
}
|
|
else
|
|
{
|
|
if (0 == dwStream)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Couldn't find which pin of int. filter corresponds to %s of Nav (Stream 0x%lx)"),
|
|
(LPCTSTR)CDisp(pPinOut), dwStreamOut)) ;
|
|
|
|
//
|
|
// HACK: (for MediaMatics 3D Audio filter)
|
|
// Most probably the 3rd party filters uses a custom
|
|
// mediatype on the out pin. We'll see if the out is
|
|
// connected at all. If not, we'll say dwStreamOut
|
|
// is not rendered; else we'll try two methods.
|
|
//
|
|
hr = pIntFPinOut->ConnectedTo(&pPinIn) ; // is it connected?
|
|
if (SUCCEEDED(hr) && pPinIn)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Out pin (%s) of int. filter corresponding to %s of Nav (Stream 0x%lx) connects to %s"),
|
|
(LPCTSTR)CDisp(pPinOut), dwStreamOut, (LPCTSTR)CDisp(pPinIn))) ;
|
|
|
|
// Method 1: If Int filter has 1 in and 1 out pin
|
|
if (m_IntFilters.GetNumInPin(pi.pFilter) == 1 &&
|
|
m_IntFilters.GetNumOutPin(pi.pFilter) == 1) // method 1 worked!!!
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Int. filter has 1 in and 1 out pin. No need to try more."))) ;
|
|
}
|
|
else // there are more than 1 in/out pin(s).
|
|
{
|
|
// Try method 2: Check mediatype of out pin of filter
|
|
// downstream of int. filter's out pin.
|
|
PIN_INFO pi2 ;
|
|
IEnumPins *pEnumPin2 ;
|
|
IPin *pPin2 ;
|
|
pPinIn->QueryPinInfo(&pi2) ;
|
|
ASSERT(pi2.pFilter) ;
|
|
pi2.pFilter->EnumPins(&pEnumPin2) ;
|
|
ASSERT(pEnumPin2) ;
|
|
dwStream = 0 ; // just to be sure
|
|
while (0 == dwStream &&
|
|
S_OK == pEnumPin2->Next(1, &pPin2, &ul) && 1 == ul)
|
|
{
|
|
pPin2->QueryDirection(&pd) ;
|
|
if (PINDIR_OUTPUT == pd) // we only care about out pins
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Downstream filter's pin %s mediatype is being determined..."),
|
|
(LPCTSTR)CDisp(pPin2))) ;
|
|
DWORD dwStream = StreamFlagFromSWPin(pPin2) ;
|
|
}
|
|
pPin2->Release() ; // done with this pin
|
|
} // end of while ()
|
|
ASSERT(dwStream) ; // I hope we didn't have another failure
|
|
pEnumPin2->Release() ; // done enum-ing
|
|
pi2.pFilter->Release() ; // else we leak
|
|
|
|
// Check if we got the matching in and out pins
|
|
if (dwStream == dwStreamOut)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Downstream filter's out pin %s matches Nav's out pin %s (Stream 0x%lx)"),
|
|
(LPCTSTR)CDisp(pPin2), (LPCTSTR)CDisp(pPinOut), dwStreamOut)) ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Downstream filter's out pin %s DOES NOT match Nav's out pin %s (Stream 0x%lx). Try more..."),
|
|
(LPCTSTR)CDisp(pPin2), (LPCTSTR)CDisp(pPinOut), dwStreamOut)) ;
|
|
|
|
// We weren't really looking for this pin;
|
|
// keep trying the other out pins.
|
|
pPinIn->Release() ;
|
|
pPinIn = NULL ;
|
|
}
|
|
}
|
|
} // end of if (connected?)
|
|
else
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("NOTE: Unconnected out pin (%s) of int. filter corresponding to %s of Nav (Stream 0x%lx)"),
|
|
(LPCTSTR)CDisp(pIntFPinOut), (LPCTSTR)CDisp(pPinOut), dwStreamOut)) ;
|
|
}
|
|
else
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Skipping non-matching pin (Stream 0x%lx) of int. filter for %s of Nav (Stream 0x%lx)"),
|
|
dwStream, (LPCTSTR)CDisp(pPinOut), dwStreamOut)) ;
|
|
}
|
|
}
|
|
pIntFPinOut->Release() ;
|
|
|
|
} // end while ()
|
|
|
|
pEnumIntFPins->Release() ; // done with pin enum
|
|
} // end of if (m_IntFilters.IsInList())
|
|
else
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Nav pin %s is NOT connected to an int. filter in our list"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
|
|
pi.pFilter->Release() ; // else we leak
|
|
}
|
|
// Otherwise pPinIn or hr indicates the "not connected" state which
|
|
// will be tested below to set the approp. flags etc. in the Status
|
|
// struct.
|
|
}
|
|
else // Nav connects directly to the decoder(s)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Pin %s is NOT connected indirectly"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
hr = pPinOut->ConnectedTo(&pPinIn) ;
|
|
}
|
|
|
|
if (/* SUCCEEDED(hr) && */ pPinIn) // already connected out pin of DVD src
|
|
{
|
|
pPinIn->Release() ; // let the in pin go
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_ERROR, 4, TEXT("NOTE: Pin %s is not connected"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
if (0 == (dwStreamOut & pStatus->dwFailedStreamsFlag)) // hasn't yet been set
|
|
{
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= dwStreamOut ;
|
|
}
|
|
hrFinal = S_FALSE ;
|
|
}
|
|
|
|
pPinOut->Release() ; // let the out pin go
|
|
} // end of while (S_OK == ...->Next())
|
|
|
|
pEnumSrcPins->Release() ; // done with src pin enum
|
|
|
|
// Check that at least one stream has been rendered right. Otherwise
|
|
// fail the method.
|
|
if (pStatus->iNumStreamsFailed >= pStatus->iNumStreams)
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("None of the %d DVD streams rendered right (failed %d)"),
|
|
pStatus->iNumStreams, pStatus->iNumStreamsFailed)) ;
|
|
hrFinal = VFW_E_DVD_RENDERFAIL ;
|
|
}
|
|
|
|
return hrFinal ;
|
|
}
|
|
|
|
|
|
//
|
|
// Build the DVD playback graph using all/most HW decoder
|
|
//
|
|
HRESULT CDvdGraphBuilder::MakeGraphHW(BOOL bHWOnly, AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::MakeGraphHW(%d, 0x%lx)"),
|
|
bHWOnly, pStatus)) ;
|
|
|
|
HRESULT hr ;
|
|
HRESULT hrFinal = S_OK ;
|
|
CListDecoders HWDecs ;
|
|
|
|
//
|
|
// If graph was previously built and we are rebuilding now (may be with
|
|
// different options) then clear the existing graph.
|
|
//
|
|
if (m_bGraphDone)
|
|
ClearGraph() ;
|
|
|
|
//
|
|
// Build using as many HW decoders as possible
|
|
//
|
|
hr = CreateDVDHWDecoders(&HWDecs) ;
|
|
if (FAILED(hr) || 0 == HWDecs.GetNumHWFilters())
|
|
{
|
|
if (bHWOnly)
|
|
{
|
|
DbgLog((LOG_ERROR, 4, TEXT("No hardware DVD decoder found, but only HW decoding asked"))) ;
|
|
return VFW_E_DVD_DECNOTENOUGH ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("No hardware DVD decoder found, HW decoding was preferred"))) ;
|
|
}
|
|
}
|
|
|
|
IBaseFilter *pSrc ;
|
|
|
|
// Instantiate DVD Nav, only if user hasn't specified a DVD source filter
|
|
if (m_pDVDSrc)
|
|
pSrc = m_pDVDSrc ;
|
|
else
|
|
{
|
|
hr = CreateFilterInGraph(CLSID_DVDNavigator, TEXT("DVD Navigator"), &m_pDVDNav) ;
|
|
if (FAILED(hr) || NULL == m_pDVDNav)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: DVD Nav couldn't be instantiated (Error 0x%lx)"), hr)) ;
|
|
return VFW_E_DVD_RENDERFAIL ;
|
|
}
|
|
pSrc = m_pDVDNav ;
|
|
}
|
|
|
|
//
|
|
// Connect DVD source filters' output pins (as many as possible) to the
|
|
// input pins of the HW decoder filters (if any).
|
|
//
|
|
hr = ConnectSrcToHWDec(pSrc, &HWDecs, pStatus) ;
|
|
if (FAILED(hr))
|
|
{
|
|
return VFW_E_DVD_RENDERFAIL ; // hr ;
|
|
}
|
|
|
|
if (S_OK != hr && !bHWOnly) // if some pins are unconnected AND only if not HW-only
|
|
{
|
|
//
|
|
// If we still have any output pin unconnected then try to find some
|
|
// SW decoder to connect to. So go through the list once more.
|
|
//
|
|
hr = ConnectSrcToSWDec(pSrc, pStatus) ;
|
|
if (FAILED(hr))
|
|
{
|
|
return VFW_E_DVD_RENDERFAIL ; // hr ;
|
|
}
|
|
}
|
|
|
|
// Check if all the output pin of DVD source have been connected. If not
|
|
// set the warning sign for the caller to catch.
|
|
hr = CheckSrcPinConnection(pSrc, pStatus) ;
|
|
if (S_OK != hr && SUCCEEDED(hrFinal))
|
|
hrFinal = hr ;
|
|
|
|
// The other DVD HW decoders will get unloaded when we exit this function
|
|
// as that will be out of scope for the HWDecs object causing its
|
|
// destructor to be called which frees all unused items in the list.
|
|
|
|
return hrFinal ;
|
|
}
|
|
|
|
|
|
//
|
|
// Build the DVD playback graph using all/most SW decoders
|
|
//
|
|
HRESULT CDvdGraphBuilder::MakeGraphSW(BOOL bSWOnly, AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::MakeGraphSW(%d, 0x%lx)"),
|
|
bSWOnly, pStatus)) ;
|
|
|
|
HRESULT hr ;
|
|
HRESULT hrFinal = S_OK ;
|
|
IBaseFilter *pSrc ;
|
|
|
|
//
|
|
// If graph was previously built and we are rebuilding now (may be with
|
|
// different options) then clear the existing graph.
|
|
//
|
|
if (m_bGraphDone)
|
|
ClearGraph() ;
|
|
|
|
// Instantiate DVD Nav, only if user hasn't specified a DVD source filter
|
|
if (m_pDVDSrc)
|
|
pSrc = m_pDVDSrc ;
|
|
else
|
|
{
|
|
hr = CreateFilterInGraph(CLSID_DVDNavigator, TEXT("DVD Navigator"), &m_pDVDNav) ;
|
|
if (FAILED(hr) || NULL == m_pDVDNav)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: DVD Nav couldn't be instantiated (Error 0x%lx)"), hr)) ;
|
|
return VFW_E_DVD_RENDERFAIL ;
|
|
}
|
|
pSrc = m_pDVDNav ;
|
|
}
|
|
|
|
//
|
|
// Connect DVD source filters' output pins (as many as possible) to the
|
|
// input pins of the SW decoder filters (if any).
|
|
//
|
|
hr = ConnectSrcToSWDec(pSrc, pStatus) ;
|
|
if (FAILED(hr))
|
|
{
|
|
return VFW_E_DVD_RENDERFAIL ; // hr ;
|
|
}
|
|
|
|
if (0 == m_Decoders.GetNumSWFilters())
|
|
{
|
|
if (bSWOnly)
|
|
{
|
|
DbgLog((LOG_ERROR, 4, TEXT("No software DVD decoder found, but only SW decoding asked"))) ;
|
|
return VFW_E_DVD_DECNOTENOUGH ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("No software DVD decoder found, SW decoding was preferred"))) ;
|
|
}
|
|
}
|
|
|
|
if (S_OK != hr && !bSWOnly) // if not all pins are connected AND if not SW-only
|
|
{
|
|
//
|
|
// If we still have any output pin unconnected then try to find some
|
|
// HW decoder to connect to. So go through the list once more.
|
|
//
|
|
CListDecoders HWDecs ;
|
|
hr = CreateDVDHWDecoders(&HWDecs) ;
|
|
if (FAILED(hr) || 0 == HWDecs.GetNumHWFilters()) // no HW decoder found
|
|
{
|
|
DbgLog((LOG_ERROR, 4, TEXT("HW Decoders couldn't be instantiated/found."))) ;
|
|
}
|
|
else // got some HW decoders; try them
|
|
{
|
|
hr = ConnectSrcToHWDec(pSrc, &HWDecs, pStatus) ;
|
|
if (FAILED(hr))
|
|
{
|
|
return VFW_E_DVD_RENDERFAIL ; // hr ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if all the output pin of DVD source have been connected. If not
|
|
// set the warning sign for the caller to catch.
|
|
hr = CheckSrcPinConnection(pSrc, pStatus) ;
|
|
if (S_OK != hr && SUCCEEDED(hrFinal))
|
|
hrFinal = hr ;
|
|
|
|
// The other DVD HW decoders will get unloaded when we exit this function
|
|
// as that will be out of scope for the HWDecs object causing its
|
|
// destructor to be called which frees all unused items in the list.
|
|
|
|
return hrFinal ;
|
|
}
|
|
|
|
|
|
#define CLSID_VideoMixer CLSID_OverlayMixer
|
|
|
|
//
|
|
// Complete the DVD playback graph by bringing in renderers etc.
|
|
//
|
|
HRESULT CDvdGraphBuilder::RenderDecoderOutput(AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderDecoderOutput(0x%lx)"), pStatus)) ;
|
|
|
|
int i ;
|
|
BOOL bHW ;
|
|
IBaseFilter *pDecFilter ;
|
|
HRESULT hr ;
|
|
IEnumPins *pEnumPins ;
|
|
IPin *pPinOut ;
|
|
ULONG ul ;
|
|
PIN_DIRECTION pd ;
|
|
LPTSTR lpszName ;
|
|
int iAll = m_Decoders.GetNumFilters() ;
|
|
int iHW = m_Decoders.GetNumHWFilters() ;
|
|
HRESULT hrFinal = S_OK ;
|
|
|
|
//
|
|
// If caller didn't object about VPE, we need to create the
|
|
// VideoMixer first and then do the Render; otherwise it fails
|
|
// anyway!!! If the caller doesn't want VPE, we'll let it
|
|
// fail by not creating VideoMixer and detect the failure using
|
|
// the DetectFailedHWPin() call.
|
|
//
|
|
// To render the video output pin. We are going to use
|
|
// overlay mixing for SW decoders
|
|
//
|
|
if (m_bUseVPE)
|
|
{
|
|
// Create VideoMixer, Line21 decoder and Video Renderer here
|
|
// (just because it's easier)
|
|
hr = CreateFilterInGraph(CLSID_VideoRenderer,
|
|
TEXT("Video Renderer"), &m_pVR) ;
|
|
ASSERT(SUCCEEDED(hr) && m_pVR) ;
|
|
if (FAILED(hr) || NULL == m_pVR)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Couldn't start Video Renderer -- no display on monitor"))) ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ; // can't show CC or Video
|
|
hrFinal = FALSE ;
|
|
}
|
|
hr = CreateFilterInGraph(CLSID_VideoMixer, TEXT("Overlay Mixer"),
|
|
&m_pVM) ;
|
|
if (FAILED(hr) || NULL == m_pVM)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("Couldn't start VideoMixer -- no mixing"))) ;
|
|
pStatus->hrVPEStatus = hr ;
|
|
hrFinal = FALSE ;
|
|
}
|
|
hr = CreateFilterInGraph(CLSID_Line21Decoder,
|
|
TEXT("Line21 Decoder"), &m_pL21Dec) ;
|
|
if (FAILED(hr) || NULL == m_pL21Dec)
|
|
{
|
|
DbgLog((LOG_ERROR, 3, TEXT("Couldn't start Line21 Dec -- no CC"))) ;
|
|
hrFinal = FALSE ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Assume that video decoder (HW/SW) doesn't produce Line21 data and
|
|
// we don't render it right until we find some proof otherway.
|
|
//
|
|
pStatus->bNoLine21In = TRUE ;
|
|
pStatus->bNoLine21Out = TRUE ;
|
|
|
|
//
|
|
// We must not connect the output of any other filter to any pin of the
|
|
// Video mixer filter (VPMixer/OverlayMixer) until the video decoder's
|
|
// output pin is connected to its primary input pin. So if the CC pin
|
|
// or Subpicture pin is enumerated before the video out pin of the decoder,
|
|
// we have to remember to render them later.
|
|
//
|
|
// We should reset these late-rendering-helper-members
|
|
//
|
|
m_dwVideoRenderStatus = 0 ;
|
|
m_pL21PinToRender = NULL ;
|
|
m_pSPPinToRender = NULL ;
|
|
|
|
// render the output pins of all the decoders used
|
|
IPin *pPinIn ;
|
|
for (i = 0 ; i < iAll ; i++)
|
|
{
|
|
if (! m_Decoders.GetFilter(i, &pDecFilter, &lpszName, &bHW) )
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("*** INTERNAL ERROR: Can't get filter from decoder list ***"))) ;
|
|
ASSERT(FALSE) ; // so that we know
|
|
continue ; // at least avoid faulting
|
|
}
|
|
ASSERT(pDecFilter) ;
|
|
hr = pDecFilter->EnumPins(&pEnumPins) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumPins) ;
|
|
pEnumPins->Reset() ;
|
|
|
|
while (S_OK == pEnumPins->Next(1, &pPinOut, &ul) && 1 == ul)
|
|
{
|
|
hr = pPinOut->QueryDirection(&pd) ;
|
|
ASSERT(SUCCEEDED(hr)) ;
|
|
if (PINDIR_INPUT == pd) // don't want input pin
|
|
{
|
|
pPinOut->Release() ; // don't need this pin
|
|
continue ;
|
|
}
|
|
hr = pPinOut->ConnectedTo(&pPinIn) ;
|
|
if (SUCCEEDED(hr) && pPinIn) // don't want connected pin
|
|
{
|
|
pPinIn->Release() ; // not interseted in it
|
|
pPinOut->Release() ; // not this one
|
|
continue ;
|
|
}
|
|
|
|
//
|
|
// If the decoder is HW based, the proxy doesn't let us enum
|
|
// the supported media types and then connect to a suitable in
|
|
// pin on the other side.
|
|
//
|
|
if (bHW)
|
|
hr = RenderHWOutPin(pPinOut, pStatus) ;
|
|
else
|
|
hr = RenderSWOutPin(pPinOut, pStatus) ;
|
|
|
|
if (S_OK != hr) // if not perfect
|
|
hrFinal = hr ; // use this error code
|
|
|
|
pPinOut->Release() ; // done with this pin
|
|
|
|
} // end of while (pEnumPins->Next()...)
|
|
|
|
pEnumPins->Release() ; // done with this enumerator
|
|
} // end of for (i)
|
|
|
|
//
|
|
// If the caller didn't object about VPE (i.e, on-screen playback) then connect
|
|
// the video mixer to the video renderer.
|
|
//
|
|
if (m_bUseVPE)
|
|
{
|
|
if (NULL == m_pVM)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Video Mixer didn't start -- no CC at least"))) ;
|
|
// pStatus->bNoLine21Out = TRUE ; // can't show CC with video
|
|
if (NULL == m_pVR)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Video Renderer didn't start!!!"))) ;
|
|
// we have already set the video stream flag in pStatus->dwFailedStreamsFlag
|
|
}
|
|
if (S_OK == hrFinal) // very unlikely that we didn't know yet
|
|
hrFinal = S_FALSE ;
|
|
}
|
|
else // we have video mixer and renderer
|
|
{
|
|
//
|
|
// We should connect the VideoMixer and the VideoRenderer ONLY IF
|
|
// connection to OverlayMixer worked. So check for video render flag.
|
|
//
|
|
if (m_dwVideoRenderStatus & VIDEO_RENDER_MIXER)
|
|
{
|
|
hr = FindOpenPin(m_pVM, PINDIR_OUTPUT, 0, &pPinOut) ;
|
|
if (SUCCEEDED(hr) && pPinOut)
|
|
{
|
|
if (SUCCEEDED(hr = TryConnect(pPinOut, m_pVR, NULL, TRUE)))
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Connected %s to Video Renderer"),
|
|
(LPCSTR) CDisp(pPinOut))) ;
|
|
}
|
|
else // Extremely unlikely, well impossible. Still...
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Couldn't connect %s to Video Renderer (Error 0x%lx)"),
|
|
(LPCSTR) CDisp(pPinOut), hr)) ;
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
|
|
hrFinal = S_FALSE ;
|
|
}
|
|
|
|
pPinOut->Release() ; // let the pin go now
|
|
}
|
|
else // Extremely unlikely, well impossible. Still...
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Couldn't find the output pin of video mixer"))) ;
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
|
|
hrFinal = S_FALSE ;
|
|
}
|
|
}
|
|
else
|
|
DbgLog((LOG_TRACE, 1, TEXT("Couldn't connect to OverlayMixer. VM and VR not connected."))) ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// There is a possibility that the Line21 decoder's out pin was not rendered
|
|
// fully, because the video pin was not connected to the mixer till then. For
|
|
// this case, we have to do the rendering now.
|
|
//
|
|
hr = CompleteLateRender(pStatus) ;
|
|
if (S_OK != hr && SUCCEEDED(hrFinal))
|
|
hrFinal = S_FALSE ; // hr?
|
|
|
|
//
|
|
// Remove any unused filters from the graph (is it necessary?)
|
|
//
|
|
RemoveUnusedFilters(pStatus) ;
|
|
|
|
//
|
|
// If there was some problem with the getting or rendering Line21 data
|
|
// then indicate the result as partial success (of course if it's not
|
|
// worse than that already :-).
|
|
//
|
|
if ((pStatus->bNoLine21In || pStatus->bNoLine21Out) && S_OK == hrFinal)
|
|
hrFinal = S_FALSE ;
|
|
|
|
//
|
|
// Last minute clean-up:
|
|
// 1. If the "No VPE" flag was specified in the call, don't bother about CC
|
|
// 2. If there was no line21 data produced by the video decoder then we
|
|
// shouldn't feel bad about not being able decode "nothing".
|
|
//
|
|
if (! m_bUseVPE )
|
|
{
|
|
pStatus->bNoLine21In = FALSE ;
|
|
pStatus->bNoLine21Out = FALSE ;
|
|
}
|
|
else if (pStatus->bNoLine21In)
|
|
pStatus->bNoLine21Out = FALSE ;
|
|
|
|
return hrFinal ;
|
|
}
|
|
|
|
|
|
//
|
|
// Check if we have the Line21 decoder and/or the Subpicture decoder's output
|
|
// pin unconnected. If so, AND if the video decoder's output pin has been
|
|
// connected already then complete the remaining pending connections.
|
|
//
|
|
HRESULT CDvdGraphBuilder::CompleteLateRender(AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::CompleteLateRender(0x%lx)"),
|
|
pStatus)) ;
|
|
|
|
HRESULT hr = NOERROR ;
|
|
|
|
if (m_dwVideoRenderStatus & VIDEO_RENDER_MIXER) // only if video connects to OverlayMixer
|
|
{
|
|
if (m_pSPPinToRender)
|
|
{
|
|
if (SUCCEEDED(hr = TryConnect(m_pSPPinToRender, m_pVM, NULL, TRUE)))
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Late render of pin %s done"),
|
|
(LPCSTR)(CDisp) m_pSPPinToRender)) ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_ERROR, 2, TEXT("WARNING: Pin %s couldn't be late rendered (Error 0x%lx)"),
|
|
(LPCSTR)(CDisp) m_pSPPinToRender, hr)) ;
|
|
hr = E_FAIL ; // some problem
|
|
}
|
|
|
|
// We are done with it. Reset the flag and pointer...
|
|
m_pSPPinToRender->Release() ;
|
|
m_pSPPinToRender = NULL ;
|
|
}
|
|
if (m_pL21PinToRender)
|
|
{
|
|
if (SUCCEEDED(hr = TryConnect(m_pL21PinToRender, m_pVM, NULL, TRUE)))
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Late render of pin %s done (Error 0x%lx)"),
|
|
(LPCSTR)(CDisp) m_pL21PinToRender, hr)) ;
|
|
pStatus->bNoLine21Out = FALSE ; // line21 data has been rendered
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_ERROR, 2, TEXT("WARNING: Pin %s couldn't be late rendered"),
|
|
(LPCSTR)(CDisp) m_pL21PinToRender)) ;
|
|
hr = E_FAIL ; // some problem
|
|
}
|
|
|
|
// We are done with it. Reset the flag and pointer...
|
|
m_pL21PinToRender->Release() ;
|
|
m_pL21PinToRender = NULL ;
|
|
}
|
|
}
|
|
else // video rendering failed somehow -- can't do SP/CC
|
|
{
|
|
if (m_pSPPinToRender)
|
|
{
|
|
// Can't do anything -- release the interface and set SP render failure flag
|
|
m_pSPPinToRender->Release() ;
|
|
m_pSPPinToRender = NULL ;
|
|
if (0 == (pStatus->dwFailedStreamsFlag & AM_DVD_STREAM_SUBPIC)) // if not set yet
|
|
{
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_SUBPIC ;
|
|
}
|
|
}
|
|
if (m_pL21PinToRender)
|
|
{
|
|
// Can't do anything -- release the interface and set CC render failure flag
|
|
m_pL21PinToRender->Release() ;
|
|
m_pL21PinToRender = NULL ;
|
|
pStatus->bNoLine21Out = TRUE ; // couldn't render
|
|
}
|
|
}
|
|
|
|
return hr ;
|
|
}
|
|
|
|
|
|
BOOL CDvdGraphBuilder::RemoveFilterIfUnused(IBaseFilter *pFilter,
|
|
LPCTSTR lpszFilterName)
|
|
{
|
|
IEnumPins *pEnumPins ;
|
|
HRESULT hr ;
|
|
IPin *pPin ;
|
|
IPin *pPinOther ;
|
|
ULONG ul ;
|
|
BOOL bInUse = FALSE ;
|
|
|
|
if (pFilter)
|
|
{
|
|
hr = pFilter->EnumPins(&pEnumPins) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumPins) ;
|
|
while (S_OK == pEnumPins->Next(1, &pPin, &ul) && 1 == ul)
|
|
{
|
|
pPin->ConnectedTo(&pPinOther) ;
|
|
if (pPinOther)
|
|
{
|
|
pPin->Release() ;
|
|
pPinOther->Release() ;
|
|
bInUse = TRUE ;
|
|
break ; // done with our search
|
|
}
|
|
pPin->Release() ;
|
|
}
|
|
pEnumPins->Release() ;
|
|
if (!bInUse)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Releasing unncessary filter %s"), lpszFilterName)) ;
|
|
m_pGB->RemoveFilter(pFilter) ;
|
|
pFilter->Release() ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Filter %s is being used"), lpszFilterName)) ;
|
|
}
|
|
}
|
|
|
|
return (! bInUse ) ; // removed / in use
|
|
}
|
|
|
|
|
|
HRESULT CDvdGraphBuilder::RemoveUnusedFilters(AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RemoveUnusedFilters(0x%lx)"),
|
|
pStatus)) ;
|
|
|
|
//
|
|
// Check if Line21 decoder, Video Mixer, Video and Audio Renderer are
|
|
// unused (and unconnected). If so, remove and free them.
|
|
//
|
|
if (RemoveFilterIfUnused(m_pVM, TEXT("Overlay Mixer")))
|
|
m_pVM = NULL ;
|
|
|
|
// Line21 decoder is removed if either
|
|
// a) it's not in use, i.e, not connected on either side or
|
|
// b) it's connected on at least one side, but bLine21In/Out flag is set
|
|
// indicating the connection wasn't completely successful and CC won't work.
|
|
if (RemoveFilterIfUnused(m_pL21Dec, TEXT("Line21 Decoder")))
|
|
m_pL21Dec = NULL ;
|
|
else if (pStatus->bNoLine21In || pStatus->bNoLine21Out && // if somehow CC won't work,
|
|
m_pL21Dec) // but we have a L21Dec
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Line21 decoder was connected, but won't work. Hence removed."))) ;
|
|
m_pGB->RemoveFilter(m_pL21Dec) ;
|
|
m_pL21Dec->Release() ;
|
|
m_pL21Dec = NULL ;
|
|
}
|
|
|
|
if (RemoveFilterIfUnused(m_pVR, TEXT("VideoRenderer")))
|
|
m_pVR = NULL ;
|
|
|
|
if (RemoveFilterIfUnused(m_pAR, TEXT("AudioRenderer")))
|
|
m_pAR = NULL ;
|
|
|
|
return NOERROR ;
|
|
}
|
|
|
|
|
|
void CDvdGraphBuilder::RenderUnknownPin(IPin *pPinOut)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderUnknownPin(0x%lx)"),
|
|
pPinOut)) ;
|
|
|
|
HRESULT hr = m_pGB->Render(pPinOut) ;
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Rendering (%s) failed (Error 0x%lx)"),
|
|
(LPCSTR) CDisp(pPinOut), hr)) ;
|
|
}
|
|
else // seems to have rendered
|
|
{
|
|
IPin *pPinIn ;
|
|
pPinOut->ConnectedTo(&pPinIn) ;
|
|
if (pPinIn)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Pin %s with unknown mediatype rendered successfully"),
|
|
(LPCSTR) CDisp(pPinOut))) ;
|
|
pPinIn->Release() ;
|
|
}
|
|
else // this may be the NTSC out pin
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Pin %s with unknown mediatype actually wasn't rendered"),
|
|
(LPCSTR) CDisp(pPinOut))) ;
|
|
// but we don't care anymore about it. So ignore it.
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CDvdGraphBuilder::RenderAudioOutPin(IPin *pPinOut, AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderAudioOutPin(0x%lx, 0x%lx)"),
|
|
pPinOut, pStatus)) ;
|
|
|
|
HRESULT hr ;
|
|
|
|
if (NULL == m_pAR)
|
|
{
|
|
hr = CreateFilterInGraph(CLSID_DSoundRender, TEXT("DSound Renderer"), &m_pAR) ;
|
|
if (FAILED(hr) || NULL == m_pAR) // couldn't instantiate audio renderer
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("Couldn't instantiate audio renderer (Error 0x%lx)"), hr)) ;
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_AUDIO ;
|
|
return hr ;
|
|
}
|
|
}
|
|
else // weird -- who started audio renderer?
|
|
ASSERT(NULL == m_pAR) ; // so that we know
|
|
|
|
hr = TryConnect(pPinOut, m_pAR, NULL, FALSE) ;
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_TRACE, 3,
|
|
TEXT("Couldn't even indirectly connect pin (%s) to audio renderer (Error 0x%lx)"),
|
|
(LPCTSTR) CDisp(pPinOut), hr)) ;
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_AUDIO ;
|
|
return hr ;
|
|
}
|
|
|
|
return NOERROR ;
|
|
}
|
|
|
|
|
|
HRESULT CDvdGraphBuilder::ConnectLine21OutPin(IPin *pPinOut)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::ConnectLine21OutPin(0x%lx)"),
|
|
pPinOut)) ;
|
|
|
|
HRESULT hr = TryConnect(pPinOut, m_pL21Dec, NULL, TRUE) ;
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("Couldn't connect Line21 out to Line21Dec (Error 0x%lx)"),
|
|
hr)) ;
|
|
return hr ;
|
|
}
|
|
|
|
DbgLog((LOG_TRACE, 5, TEXT("Line21 output connected to Line21 Dec"))) ;
|
|
|
|
ASSERT(NULL == m_pL21PinToRender) ;
|
|
hr = FindOpenPin(m_pL21Dec, PINDIR_OUTPUT, 0, &m_pL21PinToRender) ;
|
|
ASSERT(SUCCEEDED(hr) && m_pL21PinToRender) ;
|
|
|
|
//
|
|
// We should connect the Line21 Decoder output to the
|
|
// VideoMixer ONLY IF the decoded video stream has already
|
|
// been connected.
|
|
//
|
|
if (m_dwVideoRenderStatus & VIDEO_RENDER_MIXER)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Going to connect CC output to video mixer..."))) ;
|
|
if (SUCCEEDED(hr = TryConnect(m_pL21PinToRender, m_pVM, NULL, TRUE)))
|
|
DbgLog((LOG_TRACE, 5, TEXT("L21Dec output connected to video mixer"))) ;
|
|
else
|
|
DbgLog((LOG_ERROR, 1,
|
|
TEXT("WARNING: L21Dec output COULDN'T be connected to Video mixer (Error 0x%lx)"), hr)) ;
|
|
|
|
// Don't need to maintain this late rendering info anymore
|
|
m_pL21PinToRender->Release() ;
|
|
m_pL21PinToRender = NULL ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Video stream not rendered fully. L21Dec render deferred."))) ;
|
|
//
|
|
// There is a chance that we'll be able to do a late render on
|
|
// this pin after the video pin is rendered. So retain the info
|
|
// and try at last.
|
|
//
|
|
}
|
|
|
|
return hr ;
|
|
}
|
|
|
|
|
|
HRESULT CDvdGraphBuilder::RenderHWOutPin(IPin *pPinOut, AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderHWOutPin(0x%lx, 0x%lx)"),
|
|
pPinOut, pStatus)) ;
|
|
|
|
HRESULT hr ;
|
|
HRESULT hrFinal = S_OK ;
|
|
IPin *pPinIn ;
|
|
DWORD dwStream ;
|
|
|
|
dwStream = StreamFlagForHWPin(pPinOut) ;
|
|
|
|
if (m_bUseVPE) // display video (and CC) on screen
|
|
{
|
|
if (AM_DVD_STREAM_VIDEO == dwStream) // this pin is video (VPE) out
|
|
{
|
|
if (FAILED(hr = TryConnect(pPinOut, m_pVM, NULL, TRUE))) // connect VPE outpin to OverlayMixer
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Can't render pin (%s) (Error 0x%lx)"),
|
|
(LPCTSTR) CDisp(pPinOut), hr)) ;
|
|
hrFinal = S_FALSE ;
|
|
pStatus->iNumStreamsFailed++ ; // video stream not rendered right
|
|
pStatus->dwFailedStreamsFlag |= dwStream ;
|
|
pStatus->hrVPEStatus = hr ; // TryConnect() returns VPE failure code
|
|
}
|
|
else // VPE out pin connected !!!
|
|
m_dwVideoRenderStatus = VIDEO_RENDER_MIXER ;
|
|
}
|
|
else if (AM_DVD_STREAM_LINE21 == dwStream) // this pin is Line21 out
|
|
{
|
|
pStatus->bNoLine21In = FALSE ; // CC data produced by decoder
|
|
if (FAILED(hr = ConnectLine21OutPin(pPinOut))) // connect to Line21 Decoder
|
|
{
|
|
hrFinal = S_FALSE ;
|
|
pStatus->bNoLine21Out = TRUE ; // line21 couldn't be rendered
|
|
}
|
|
else // Line21 out pin connected !!!
|
|
pStatus->bNoLine21Out = FALSE ;
|
|
} // end of else if (dwStream == .._LINE21)
|
|
else if (AM_DVD_STREAM_AUDIO == dwStream) // audio out pin (very unlikely)
|
|
{
|
|
hr = RenderAudioOutPin(pPinOut, pStatus) ;
|
|
if (FAILED(hr))
|
|
hrFinal = S_FALSE ;
|
|
}
|
|
else // didn't connect to known filters -- try to just render
|
|
{
|
|
RenderUnknownPin(pPinOut) ;
|
|
} // end of else of if (AM_DVD_STREAM_... == dwStream)
|
|
}
|
|
else // w/o using VPE, i.e, on-screen display
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("User doesn't want VPE - no Video/CC to be rendered"))) ;
|
|
if (AM_DVD_STREAM_VIDEO == dwStream)
|
|
; // just ignore
|
|
else if (AM_DVD_STREAM_LINE21 == dwStream)
|
|
{
|
|
pStatus->bNoLine21In = FALSE ;
|
|
pStatus->bNoLine21Out = FALSE ;
|
|
}
|
|
else if (AM_DVD_STREAM_AUDIO == dwStream)
|
|
{
|
|
hr = RenderAudioOutPin(pPinOut, pStatus) ;
|
|
if (FAILED(hr))
|
|
hrFinal = S_FALSE ;
|
|
}
|
|
else // don't know the media type
|
|
{
|
|
RenderUnknownPin(pPinOut) ;
|
|
}
|
|
}
|
|
|
|
return hrFinal ;
|
|
}
|
|
|
|
|
|
HRESULT CDvdGraphBuilder::RenderSWOutPin(IPin *pPinOut, AM_DVD_RENDERSTATUS *pStatus)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderSWOutPin(0x%lx, 0x%lx)"),
|
|
pPinOut, pStatus)) ;
|
|
|
|
IEnumMediaTypes *pEnumMT ;
|
|
ULONG ul ;
|
|
AM_MEDIA_TYPE *pmt ;
|
|
HRESULT hr ;
|
|
HRESULT hrFinal = S_OK ;
|
|
|
|
hr = pPinOut->EnumMediaTypes(&pEnumMT) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumMT) ;
|
|
pEnumMT->Reset() ;
|
|
hr = pEnumMT->Next(1, &pmt, &ul) ; // getting 1st media type is fine
|
|
ASSERT(SUCCEEDED(hr) && 1 == ul) ;
|
|
if (pmt->majortype == MEDIATYPE_Video)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Going to render 'video' out pin (%s) of decoder"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
|
|
//
|
|
// The SW video and SP decoder both output video majortype samples.
|
|
// So we need to distinguish them before we try to connect them and
|
|
// act accordingly.
|
|
//
|
|
DWORD dwStream = GetInTypeForVideoOutPin(pPinOut) ;
|
|
if (AM_DVD_STREAM_VIDEO != dwStream &&
|
|
AM_DVD_STREAM_SUBPIC != dwStream)
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: Video out pin not from video or subpic in pin"))) ;
|
|
goto FinalExit ; // I hate "goto", but otherwise it's too complex
|
|
}
|
|
|
|
if (NULL == m_pVM)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Couldn't start VideoMixer -- no mixing"))) ;
|
|
pStatus->bNoLine21Out = TRUE ;
|
|
if (AM_DVD_STREAM_SUBPIC == dwStream)
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Subpic stream not rendered because no videomixer"))) ;
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_SUBPIC ;
|
|
}
|
|
else if (AM_DVD_STREAM_VIDEO == dwStream)
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Video stream not rendered because no videomixer"))) ;
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Unknown stream (Id: 0x%x) not rendered"), dwStream)) ;
|
|
}
|
|
#if 0
|
|
else if (SUCCEEDED(hr = TryConnect(pPinOut, m_pVR, NULL, TRUE)))
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Video decoder's output connected to VR"))) ;
|
|
// Though the Video stream has been rendered here, we don't
|
|
// go through the VideoMixer and hence can't mix the CC (and SP, if any)
|
|
// stream. So we set the m_dwVideoRenderStatus value to VIDEO_RENDER_VR,
|
|
// which is checked before Line21 Decoder output is connected.
|
|
// (I know it's kind of hacky, but...).
|
|
m_dwVideoRenderStatus = VIDEO_RENDER_VR ; // connected to VR directly
|
|
}
|
|
else // very weird, but...
|
|
{
|
|
DbgLog((LOG_ERROR, 1,
|
|
TEXT("WARNING: Video decoder's output COULDN'T be connected to VR (Error 0x%lx)"), hr)) ;
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
|
|
}
|
|
#endif // #if 0
|
|
hrFinal = S_FALSE ; // not perfect, but...
|
|
}
|
|
else // VideoMixer started fine; try to connect
|
|
{
|
|
//
|
|
// We should connect the decoded subpicture stream to the
|
|
// VideoMixer ONLY IF the decoded video stream has already
|
|
// been connected.
|
|
//
|
|
if (AM_DVD_STREAM_SUBPIC == dwStream)
|
|
{
|
|
if (m_dwVideoRenderStatus & VIDEO_RENDER_MIXER)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Going to connect subpic output to video mixer..."))) ;
|
|
if (SUCCEEDED(hr = TryConnect(pPinOut, m_pVM, NULL, TRUE)))
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("subpic output connected to video mixer's 2nd+ in pin"))) ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_ERROR, 1,
|
|
TEXT("WARNING: Subpic output COULDN'T be connected to Video mixer (Error 0x%lx)"), hr)) ;
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_SUBPIC ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Video stream not rendered fully. Subpic render deferred."))) ;
|
|
//
|
|
// We'll do a late render on this pin after the video pin is
|
|
// rendered. So retain the info and try at last.
|
|
//
|
|
m_pSPPinToRender = pPinOut ; // maintain this out pin as SP out pin
|
|
m_pSPPinToRender->AddRef() ; // because we release it after late render
|
|
}
|
|
}
|
|
else // it's the decoded video output pin
|
|
{
|
|
// Make sure that the video out pin is not already connected,
|
|
// in which case the following connection to VR is unnecessary
|
|
// (and will fail causing a wrong flag to be set).
|
|
if (m_dwVideoRenderStatus & VIDEO_RENDER_MIXER)
|
|
{
|
|
// We should not hit this code at all. But if we ever do,
|
|
// just get out of here.
|
|
DbgLog((LOG_TRACE, 0,
|
|
TEXT("WARNING: Pin <%s> is video? But video is already connected to mixer!!"),
|
|
(LPCSTR) CDisp(pPinOut))) ;
|
|
}
|
|
else // let's try to connect video to OverlayMixer
|
|
{
|
|
if (SUCCEEDED(hr = TryConnect(pPinOut, m_pVM, NULL, TRUE))) // set video to mixer flag
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Video Dec output connected to VideoMixer"))) ;
|
|
m_dwVideoRenderStatus = VIDEO_RENDER_MIXER ; // video pin connected to mixer
|
|
}
|
|
else // try to connect video to VR as last resort
|
|
{
|
|
DbgLog((LOG_ERROR, 1,
|
|
TEXT("WARNING: Video Dec output COULDN'T be connected to VideoMixer (Error 0x%lx)"), hr)) ;
|
|
pStatus->hrVPEStatus = hr ; // pass the error code up the app
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
|
|
m_dwVideoRenderStatus = VIDEO_RENDER_FAILED ; // just can't render video
|
|
hrFinal = S_FALSE ; // not perfect, but...
|
|
|
|
// We won't try connecting to the VR directly in this case.
|
|
#if 0
|
|
if (SUCCEEDED(hr = TryConnect(pPinOut, m_pVR, NULL, TRUE))) // just try to connect to video renderer
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Video decoder's output directly connected to VR"))) ;
|
|
// Though the Video stream has been rendered here, we don't
|
|
// go through the VideoMixer and hence can't mix the CC (and SP, if any)
|
|
// stream. So we set the m_dwVideoRenderStatus value to VIDEO_RENDER_VR
|
|
// so that we know that we got the video out pin, but CC / SP streams
|
|
// can't be mixed/rendered.
|
|
m_dwVideoRenderStatus = VIDEO_RENDER_VR ; // video is rendered, but....
|
|
}
|
|
else // very very weird
|
|
{
|
|
DbgLog((LOG_ERROR, 1,
|
|
TEXT("WARNING: Video decoder's output COULDN'T be connected to VR (Error 0x%lx)"), hr)) ;
|
|
pStatus->iNumStreamsFailed++ ;
|
|
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
|
|
m_dwVideoRenderStatus = VIDEO_RENDER_FAILED ; // just can't render video
|
|
}
|
|
#endif // #if 0
|
|
} // end of else of if (TryConnect(.., VM, ..)
|
|
} // end of else of if (m_dwVideoRenderStatus & ..MIXER)
|
|
} // end of else of if (subpicture stream)
|
|
} // end of else of if (no VideoMixer)
|
|
} // end of if (mediatype is video)
|
|
else if (pmt->majortype == MEDIATYPE_Audio)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Going to render 'audio' out pin (%s) of decoder"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
|
|
hr = RenderAudioOutPin(pPinOut, pStatus) ;
|
|
if (FAILED(hr))
|
|
hrFinal = S_FALSE ; // not perfect, but...
|
|
} // end of if (mediatype is audio)
|
|
else if (pmt->majortype == MEDIATYPE_AUXLine21Data)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Going to render 'line21' out pin (%s) of decoder"),
|
|
(LPCTSTR)CDisp(pPinOut))) ;
|
|
|
|
pStatus->bNoLine21In = FALSE ; // got line21 data input
|
|
|
|
//
|
|
// When the video is being decoded in SW and the caller doesn't
|
|
// want to use VPE (i.e, not on monitor), we can't show CC too;
|
|
// there is no point trying to render the Line21 output pin.
|
|
//
|
|
if (NULL == m_pVM)
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("WARNING: not Video Mixer => no CC rendering"))) ;
|
|
hrFinal = S_FALSE ; // not perfect, but...
|
|
pStatus->bNoLine21Out = TRUE ; // CC not available
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There is a Video Mixer. So we should decode Line21 data and mix
|
|
// it with the video stream.
|
|
//
|
|
if (FAILED(hr = ConnectLine21OutPin(pPinOut))) // connect to Line21 Decoder
|
|
{
|
|
hrFinal = S_FALSE ;
|
|
pStatus->bNoLine21Out = TRUE ; // line21 couldn't be rendered
|
|
}
|
|
else // Line21 out pin connected !!!
|
|
pStatus->bNoLine21Out = FALSE ;
|
|
} // end of else of if (NULL == m_pVM)
|
|
} // end of else if (majortype == AUXLine21Data)
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("WARNING: a non-audio/video/line21 out pin!!"))) ;
|
|
}
|
|
|
|
FinalExit:
|
|
DeleteMediaType(pmt) ;
|
|
pEnumMT->Release() ; // done with Media Type enum
|
|
|
|
return hrFinal ;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Helper function to detect a (in) pin from its connection mediatype
|
|
//
|
|
DWORD CDvdGraphBuilder::GetStreamFromMediaType(IPin *pPin)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::GetStreamFromMediaType(%s)"),
|
|
(LPCSTR) CDisp(pPin))) ;
|
|
|
|
DWORD dwStream = 0 ;
|
|
AM_MEDIA_TYPE mt ;
|
|
|
|
HRESULT hr = pPin->ConnectionMediaType(&mt) ;
|
|
ASSERT(SUCCEEDED(hr)) ;
|
|
if ( mt.majortype == MEDIATYPE_MPEG2_PES ||
|
|
mt.majortype == MEDIATYPE_DVD_ENCRYPTED_PACK ||
|
|
mt.majortype == MEDIATYPE_Video )
|
|
{
|
|
if (mt.subtype == MEDIASUBTYPE_MPEG2_VIDEO)
|
|
dwStream = AM_DVD_STREAM_VIDEO ;
|
|
else if (mt.subtype == MEDIASUBTYPE_DVD_SUBPICTURE)
|
|
dwStream = AM_DVD_STREAM_SUBPIC ;
|
|
else
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: Non-video/SP subtype for video data!!!"))) ;
|
|
}
|
|
else if ( mt.majortype == MEDIATYPE_MPEG2_PES ||
|
|
mt.majortype == MEDIATYPE_DVD_ENCRYPTED_PACK ||
|
|
mt.majortype == MEDIATYPE_Audio )
|
|
{
|
|
if (mt.subtype == MEDIASUBTYPE_DOLBY_AC3)
|
|
dwStream = AM_DVD_STREAM_AUDIO ;
|
|
else
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: Non-audio subtype for audio data!!!"))) ;
|
|
}
|
|
else
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: Unknown media type!!!"))) ;
|
|
|
|
FreeMediaType(mt) ; // release the mediatype
|
|
|
|
return dwStream ;
|
|
}
|
|
|
|
|
|
//
|
|
// Given a video output pin, we try to figure out if the input type is video
|
|
// (and subpic) or if it is subpic only. Because based on that we'll do
|
|
// immediate or deferred pin connect.
|
|
//
|
|
DWORD CDvdGraphBuilder::GetInTypeForVideoOutPin(IPin *pPinOut)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::GetInTypeForVideoOutPin(%s)"), (LPCSTR) CDisp(pPinOut))) ;
|
|
|
|
if (NULL == pPinOut)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Can't get input type for a NULL out pin"),
|
|
(LPCSTR) CDisp(pPinOut))) ;
|
|
return 0 ;
|
|
}
|
|
|
|
HRESULT hr ;
|
|
DWORD dwStream = 0 ;
|
|
|
|
//
|
|
// See if the decoder has any in pin to out pin internal connection info.
|
|
//
|
|
// NOTE:
|
|
// We only expect that in case the out pin has internal connections to more
|
|
// than 1 in pins then the real in pin be listed first by
|
|
// QueryInternalConnection() implementation.
|
|
//
|
|
ULONG ulNum = 0 ;
|
|
IPin *apPinIn[5] ; // 5 is way too much here
|
|
hr = pPinOut->QueryInternalConnections(apPinIn, &ulNum) ;
|
|
// I am not sure if we should check for S_FALSE or S_OK return from the
|
|
// above call. So I am using SUCCEEDED().
|
|
if (SUCCEEDED(hr) && ulNum > 0) // Yahoo!! QueryInternalConnection is supported!!!
|
|
{
|
|
DbgLog((LOG_TRACE, 3,
|
|
TEXT("Using QueryInternalConnections() to detect in pin of <%s>"),
|
|
(LPCTSTR) CDisp(pPinOut))) ;
|
|
ASSERT(ulNum <= 5) ; // just to be sure
|
|
hr = pPinOut->QueryInternalConnections(apPinIn, &ulNum) ;
|
|
if (S_OK == hr)
|
|
{
|
|
if (ulNum > 1)
|
|
DbgLog((LOG_TRACE, 3,
|
|
TEXT("** We expect the real in pin to be listed first **"))) ;
|
|
|
|
dwStream = GetStreamFromMediaType(apPinIn[0]) ; // look at 1st pin only
|
|
|
|
// Remember to release the IPin interfaces before leaving
|
|
for (ULONG ul = 0 ; ul < ulNum ; ul++)
|
|
apPinIn[ul]->Release() ;
|
|
|
|
if (dwStream) // if we know what it is...
|
|
return dwStream ; // ...we are done.
|
|
}
|
|
}
|
|
|
|
//
|
|
// The filter doesn't give any in pin to out pin internal connection info.
|
|
// So we have to (kind of hack to) find out what type in pin its filter
|
|
// has; there is a possibility that there will be more than 1 in pin in the
|
|
// filter (as in MediaMatics case), in which case we have to trust that
|
|
// the video in pin enumerates first, failing which we'll mistake the video out
|
|
// pin as the SP out pin and connection may fail.
|
|
//
|
|
PIN_INFO pi ;
|
|
hr = pPinOut->QueryPinInfo(&pi) ;
|
|
ASSERT(SUCCEEDED(hr)) ;
|
|
if (NULL == pi.pFilter)
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: Out pin's filter pointer is NULL"),
|
|
(LPCSTR) CDisp(pPinOut))) ;
|
|
return 0 ;
|
|
}
|
|
IEnumPins *pEnumPins ;
|
|
hr = pi.pFilter->EnumPins(&pEnumPins) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumPins) ;
|
|
ULONG ul ;
|
|
IPin *pPin ;
|
|
PIN_DIRECTION pd ;
|
|
DWORD dw ;
|
|
BOOL bVideoIgnored = FALSE ; // a way to remember we found a video in pin
|
|
while ( 0 == dwStream &&
|
|
S_OK == pEnumPins->Next(1, &pPin, &ul) && 1 == ul)
|
|
{
|
|
hr = pPin->QueryDirection(&pd) ;
|
|
ASSERT(SUCCEEDED(hr)) ;
|
|
if (PINDIR_INPUT == pd)
|
|
{
|
|
dw = GetStreamFromMediaType(pPin) ;
|
|
if (AM_DVD_STREAM_VIDEO == dw) // We got a video in pin
|
|
{
|
|
// HACK: **
|
|
// We want to make sure we are not mistaking the SP out as
|
|
// video out, but there is no good way left.
|
|
if (VIDEO_RENDER_NONE != m_dwVideoRenderStatus) // we already got the video out pin
|
|
{
|
|
DbgLog((LOG_TRACE, 1,
|
|
TEXT("We have already got a video out pin. Try after <%s>..."),
|
|
(LPCTSTR) CDisp(pPin))) ;
|
|
bVideoIgnored = TRUE ;
|
|
}
|
|
else // this is "most likely" to be the video stream
|
|
dwStream = AM_DVD_STREAM_VIDEO ;
|
|
}
|
|
else if (AM_DVD_STREAM_AUDIO == dw ||
|
|
AM_DVD_STREAM_SUBPIC == dw)
|
|
dwStream = dw ;
|
|
else
|
|
DbgLog((LOG_TRACE, 1, TEXT("WARNING: Unknown stream (%lu) found"), dw)) ;
|
|
} // end of if (pd == INPUT)
|
|
|
|
pPin->Release() ; // let the pin go
|
|
}
|
|
pEnumPins->Release() ;
|
|
pi.pFilter->Release() ;
|
|
|
|
// (Corresponding) HACK: **
|
|
// if we ignored the video stream, recognize that here
|
|
if (0 == dwStream && bVideoIgnored)
|
|
dwStream = AM_DVD_STREAM_VIDEO ;
|
|
|
|
return dwStream ;
|
|
}
|
|
|
|
|
|
//
|
|
// Try connecting a given output pin to some input pin of the given filter
|
|
// that supports the given mediatype
|
|
//
|
|
HRESULT CDvdGraphBuilder::TryConnect(IPin *pPinOut, IBaseFilter *pFilter,
|
|
CMediaType *pmt, BOOL bDirect)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::TryConnect(0x%lx, 0x%lx, 0x%lx, %d)"),
|
|
pPinOut, pFilter, pmt, bDirect)) ;
|
|
|
|
HRESULT hr ;
|
|
HRESULT hrFinal = E_FAIL ; // suspected at first
|
|
IEnumPins *pEnumPins ;
|
|
ULONG ul ;
|
|
IPin *pPinIn ;
|
|
IPin *pPin ;
|
|
PIN_DIRECTION pd ;
|
|
|
|
if (NULL == pFilter)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Can't connect %s to a NULL filter"),
|
|
(LPCSTR) CDisp(pPinOut))) ;
|
|
return E_INVALIDARG ;
|
|
}
|
|
if (NULL == pPinOut)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Can't connect a NULL out pin to a filter"))) ;
|
|
return E_INVALIDARG ;
|
|
}
|
|
|
|
hr = pFilter->EnumPins(&pEnumPins) ;
|
|
if (FAILED(hr) || NULL == pEnumPins)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: pin enum on filter failed!!"))) ;
|
|
return E_UNEXPECTED ;
|
|
}
|
|
|
|
while (FAILED(hrFinal) &&
|
|
S_OK == pEnumPins->Next(1, &pPinIn, &ul) && 1 == ul)
|
|
{
|
|
hr = pPinIn->QueryDirection(&pd) ;
|
|
ASSERT(SUCCEEDED(hr)) ;
|
|
if (PINDIR_INPUT == pd)
|
|
{
|
|
hr = pPinIn->ConnectedTo(&pPin) ; // check if pin is already connected
|
|
if (FAILED(hr) || NULL == pPin) // only if not yet connected...
|
|
{
|
|
if (bDirect) // must connect directly
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Connect direct out pin (%s) to (%s)"),
|
|
(LPCSTR) CDisp(pPinOut), (LPCSTR) CDisp(pPinIn))) ;
|
|
hr = m_pGB->ConnectDirect(pPinOut, pPinIn, NULL) ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Connect indirect out pin (%s) to (%s)"),
|
|
(LPCSTR) CDisp(pPinOut), (LPCSTR) CDisp(pPinIn))) ;
|
|
hr = m_pGB->Connect(pPinOut, pPinIn) ;
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Connected out pin to a pin of the given filter"))) ;
|
|
hrFinal = S_OK ; // we connected!!!
|
|
}
|
|
else
|
|
hrFinal = hr ; // this is the actual error
|
|
}
|
|
else // in pin already connected, skip it.
|
|
pPin->Release() ; // we don't need the other pin anyway
|
|
}
|
|
pPinIn->Release() ; // done with this pin
|
|
}
|
|
pEnumPins->Release() ;
|
|
|
|
return hrFinal ;
|
|
}
|
|
|
|
|
|
//
|
|
// Instantiate all the HW decoders registered under DVD Hardware Decoder
|
|
// group under the Active Filters category.
|
|
//
|
|
|
|
HRESULT CDvdGraphBuilder::CreateDVDHWDecoders(CListDecoders *pHWDecList)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::CreateDVDHWDecoders(0x%lx)"),
|
|
pHWDecList)) ;
|
|
|
|
HRESULT hr ;
|
|
ICreateDevEnum *pCreateDevEnum ;
|
|
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_ICreateDevEnum, (void**)&pCreateDevEnum) ;
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: Couldn't create system dev enum (Error 0x%lx)"), hr)) ;
|
|
return hr ;
|
|
}
|
|
|
|
IEnumMoniker *pEnumMon ;
|
|
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_DVDHWDecodersCategory,
|
|
&pEnumMon, 0) ;
|
|
pCreateDevEnum->Release() ;
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("WARNING: Couldn't create class enum for DVD HW Dec category (Error 0x%lx)"),
|
|
hr)) ;
|
|
return E_FAIL ;
|
|
}
|
|
|
|
hr = pEnumMon->Reset() ;
|
|
|
|
ULONG ul ;
|
|
IMoniker *pMon ;
|
|
TCHAR achName[50] ; // big enough??
|
|
TCHAR achFriendlyName[50] ;
|
|
TCHAR achClsid[50] ;
|
|
while(S_OK == pEnumMon->Next(1, &pMon, &ul) && 1 == ul)
|
|
{
|
|
#if 0
|
|
WCHAR *wszName ;
|
|
pMon->GetDisplayName(0, 0, &wszName) ;
|
|
#ifdef UNICODE
|
|
lstrcpy(achName, wszName) ;
|
|
#else
|
|
WideCharToMultiByte(CP_ACP, 0, wszName, -1, achName, 50, NULL, NULL) ;
|
|
#endif // #if UNICODE
|
|
DbgLog((LOG_TRACE, 5, TEXT("Moniker enum: %s"), achName)) ;
|
|
CoTaskMemFree(wszName) ;
|
|
#endif // #if 0
|
|
|
|
IBaseFilter *pFilter ;
|
|
hr = pMon->BindToObject(0, 0, IID_IBaseFilter, (void**)&pFilter) ;
|
|
if (FAILED(hr) || NULL == pFilter)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Couldn't create HW dec filter (Error 0x%lx)"), hr)) ;
|
|
pMon->Release() ;
|
|
continue ;
|
|
}
|
|
DbgLog((LOG_TRACE, 5, TEXT("HW decoder filter found"))) ;
|
|
|
|
IPropertyBag *pPropBag ;
|
|
pMon->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag) ;
|
|
if(pPropBag)
|
|
{
|
|
{
|
|
VARIANT var ;
|
|
var.vt = VT_EMPTY ;
|
|
pPropBag->Read(L"DevicePath", &var, 0) ;
|
|
#ifdef UNICODE
|
|
lstrcpy(achName, var.bstrVal) ;
|
|
#else
|
|
WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, achName, 50, NULL, NULL) ;
|
|
#endif // #if UNICODE
|
|
DbgLog((LOG_TRACE, 5, TEXT("DevicePath: %s"), achName)) ;
|
|
}
|
|
|
|
{
|
|
VARIANT var;
|
|
var.vt = VT_EMPTY;
|
|
pPropBag->Read(L"FriendlyName", &var, 0);
|
|
#ifdef UNICODE
|
|
lstrcpy(achName, var.bstrVal) ;
|
|
#else
|
|
WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, achFriendlyName, 50, NULL, NULL) ;
|
|
#endif // #if UNICODE
|
|
DbgLog((LOG_TRACE, 5, TEXT("FriendlyName: %s"), achFriendlyName)) ;
|
|
}
|
|
|
|
{
|
|
VARIANT var;
|
|
var.vt = VT_EMPTY;
|
|
pPropBag->Read(L"CLSID", &var, 0);
|
|
#ifdef UNICODE
|
|
lstrcpy(achClsid, var.bstrVal) ;
|
|
#else
|
|
WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, achClsid, 50, NULL, NULL) ;
|
|
#endif // #if UNICODE
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLSID: %s"), achClsid)) ;
|
|
}
|
|
|
|
//
|
|
// We have got a device under the required category. The proxy
|
|
// for it is already instantiated. So add to the list of HW
|
|
// decoders to be used for building the graph.
|
|
//
|
|
pHWDecList->AddFilter(pFilter, achFriendlyName, TRUE, NULL) ;
|
|
|
|
pPropBag->Release() ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: BindToStorage failed"))) ;
|
|
}
|
|
|
|
pMon->Release() ;
|
|
} // end of while()
|
|
|
|
pEnumMon->Release() ;
|
|
|
|
DbgLog((LOG_TRACE, 5, TEXT("Found total %d HW decoders"), pHWDecList->GetNumHWFilters())) ;
|
|
|
|
return NOERROR ;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Get the stream mediatype flag from the pin of the SW decoder.
|
|
// It's a hack to guess the media stream.
|
|
//
|
|
DWORD CDvdGraphBuilder::StreamFlagFromSWPin(IPin *pPin)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::StreamFlagFromSWPin(%s)"),
|
|
(LPCTSTR)CDisp(pPin))) ;
|
|
|
|
AM_MEDIA_TYPE *pmt ;
|
|
IEnumMediaTypes *pEnumMT ;
|
|
HRESULT hr = pPin->EnumMediaTypes(&pEnumMT) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumMT) ;
|
|
ULONG ul ;
|
|
DWORD dwStream = 0 ;
|
|
while (0 == dwStream && // we haven't got a known mediatype
|
|
S_OK == pEnumMT->Next(1, &pmt, &ul) && 1 == ul) // more mediatypes
|
|
{
|
|
// Decipher the mediatype
|
|
if (pmt->majortype == MEDIATYPE_MPEG2_PES ||
|
|
pmt->majortype == MEDIATYPE_DVD_ENCRYPTED_PACK)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is MPEG2_PES/DVD_ENCRYPTED_PACK"))) ;
|
|
|
|
if (pmt->subtype == MEDIASUBTYPE_MPEG2_VIDEO)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Subtype is MPEG2_VIDEO"))) ;
|
|
dwStream = AM_DVD_STREAM_VIDEO ;
|
|
}
|
|
else if (pmt->subtype == MEDIASUBTYPE_DOLBY_AC3)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Subtype is DOLBY_AC3"))) ;
|
|
dwStream = AM_DVD_STREAM_AUDIO ;
|
|
}
|
|
else if (pmt->subtype == MEDIASUBTYPE_DVD_SUBPICTURE)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Subtype is DVD_Subpicture"))) ;
|
|
dwStream = AM_DVD_STREAM_SUBPIC ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Unknown subtype %s"),
|
|
(LPCSTR) CDisp(pmt->subtype))) ;
|
|
}
|
|
}
|
|
|
|
#if 0 // NOT needed for now
|
|
else if (pmt->majortype == MEDIATYPE_AUX_Line21Data)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is Line21 data"))) ;
|
|
|
|
if (pmt->subtype == MEDIASUBTYPE_Line21_GOPPacket ||
|
|
pmt->subtype == MEDIASUBTYPE_Line21_BytePair)
|
|
dwStream = AM_DVD_STREAM_LINE21 ;
|
|
}
|
|
#endif // #if 0
|
|
|
|
else if (pmt->majortype == MEDIATYPE_Video) // elementary stream
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is Video elementary"))) ;
|
|
|
|
if (pmt->subtype == MEDIASUBTYPE_DVD_SUBPICTURE)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Subtype is DVD_SUBPICTURE"))) ;
|
|
dwStream = AM_DVD_STREAM_SUBPIC ;
|
|
}
|
|
else if (pmt->subtype == MEDIASUBTYPE_MPEG2_VIDEO)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Subtype is MPEG2_VIDEO"))) ;
|
|
dwStream = AM_DVD_STREAM_VIDEO ;
|
|
}
|
|
else
|
|
DbgLog((LOG_TRACE, 5, TEXT("WARNING: Unknown subtype %s"),
|
|
(LPCSTR) CDisp(pmt->subtype))) ;
|
|
}
|
|
else if (pmt->majortype == MEDIATYPE_Audio) // elementary stream
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is Audio elementary"))) ;
|
|
dwStream = AM_DVD_STREAM_AUDIO ;
|
|
}
|
|
//
|
|
// There is a chance that some IHV/ISV creates a private mediatype
|
|
// (major or sub) as in the case of IBM (for CSS filter). We have to
|
|
// search the parts of the mediatype to locate something we recognize.
|
|
//
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 2,
|
|
TEXT("Unknown mediatype %s:%s -- may be IHV/ISV-specific mediatype"),
|
|
(LPCSTR) CDisp(pmt->majortype), (LPCSTR) CDisp(pmt->subtype))) ;
|
|
if (pmt->subtype == MEDIASUBTYPE_DOLBY_AC3)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is ISV/IHV-specific Audio"))) ;
|
|
dwStream = AM_DVD_STREAM_AUDIO ;
|
|
}
|
|
else if (pmt->subtype == MEDIASUBTYPE_MPEG2_VIDEO)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is ISV/IHV-specific Video"))) ;
|
|
dwStream = AM_DVD_STREAM_VIDEO ;
|
|
}
|
|
else if (pmt->subtype == MEDIASUBTYPE_DVD_SUBPICTURE)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is ISV/IHV-specific Subpicture"))) ;
|
|
dwStream = AM_DVD_STREAM_SUBPIC ;
|
|
}
|
|
else
|
|
{
|
|
// As of today, we don't know of any other media stream stype.
|
|
// If more becomes known, we'll add them here and create a flag
|
|
// in the AM_DVD_STREAM_FLAGS type.
|
|
DbgLog((LOG_TRACE, 2, TEXT("WARNING: Unknown mediatype. Couldn't detect at all."))) ;
|
|
}
|
|
}
|
|
|
|
DeleteMediaType(pmt) ;
|
|
} // end of while()
|
|
pEnumMT->Release() ;
|
|
|
|
return dwStream ;
|
|
}
|
|
|
|
|
|
DWORD CDvdGraphBuilder::StreamFlagForHWPin(IPin *pPin)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CDvdGraphBuilder::StreamFlagForHWPin(0x%lx)"), pPin)) ;
|
|
|
|
ASSERT(pPin) ; // so that we know
|
|
if (NULL == pPin)
|
|
return 0 ;
|
|
|
|
DWORD dwStream = 0 ;
|
|
ULONG ul ;
|
|
AM_MEDIA_TYPE *pmt ;
|
|
IEnumMediaTypes *pEnumMT ;
|
|
HRESULT hr = pPin->EnumMediaTypes(&pEnumMT) ;
|
|
ASSERT(SUCCEEDED(hr) && pEnumMT) ;
|
|
while (0 == dwStream &&
|
|
S_OK == pEnumMT->Next(1, &pmt, &ul) && 1 == ul) // more mediatypes
|
|
{
|
|
if (pmt->majortype == MEDIATYPE_Video)
|
|
dwStream = AM_DVD_STREAM_VIDEO ;
|
|
else if (pmt->majortype == MEDIATYPE_Audio)
|
|
dwStream = AM_DVD_STREAM_AUDIO ;
|
|
else if (pmt->majortype == MEDIATYPE_AUXLine21Data)
|
|
dwStream = AM_DVD_STREAM_LINE21 ;
|
|
else
|
|
DbgLog((LOG_TRACE, 3, TEXT("Unknown mediatype (%s:%s) for HW pin (%s)"),
|
|
(LPCTSTR) CDisp(pmt->majortype), (LPCTSTR) CDisp(pmt->subtype),
|
|
(LPCTSTR) CDisp(pPin))) ;
|
|
|
|
DeleteMediaType(pmt) ; // dne with this MT
|
|
} // end of while()
|
|
pEnumMT->Release() ; // done with MT enum
|
|
|
|
return dwStream ;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Implementation of the CListDecoders class...
|
|
//
|
|
|
|
CListDecoders::CListDecoders(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListDecoders::CListDecoders()"))) ;
|
|
|
|
m_iCount = 0 ;
|
|
m_iHWCount = 0 ;
|
|
for (int i = 0 ; i < DECLIST_MAX ; i++)
|
|
{
|
|
m_apFilters[i] = NULL ;
|
|
m_alpszName[i] = NULL ;
|
|
m_apClsid[i] = NULL ;
|
|
m_abIsHW[i] = FALSE ;
|
|
}
|
|
}
|
|
|
|
|
|
CListDecoders::~CListDecoders(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListDecoders::~CListDecoders()"))) ;
|
|
|
|
// We don't want to do CleanAll() here to avoid releasing the decoders
|
|
// that might be in use. The decoders are freed in the DVD Graph Builder
|
|
// object's destructor as we don't need the decoders any more.
|
|
FreeAllMem() ;
|
|
}
|
|
|
|
|
|
void CListDecoders::CleanAll(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListDecoders::CleanAll()"))) ;
|
|
|
|
// don't have a pointer to the graph, so cache it here from the
|
|
// the first filter.
|
|
IFilterGraph *pGraph = 0;
|
|
|
|
for (int i = 0 ; i < m_iCount ; i++)
|
|
{
|
|
if (m_apFilters[i])
|
|
{
|
|
if(pGraph == 0)
|
|
{
|
|
FILTER_INFO fi;
|
|
HRESULT hr = m_apFilters[i]->QueryFilterInfo(&fi);
|
|
pGraph = fi.pGraph;
|
|
|
|
ASSERT(pGraph || FAILED(hr)); // filter not on our list if not in graph
|
|
}
|
|
if(pGraph) {
|
|
EXECUTE_ASSERT(SUCCEEDED(pGraph->RemoveFilter(m_apFilters[i])));
|
|
}
|
|
|
|
DbgLog((LOG_TRACE, 5, TEXT("Going to release decoder #%d"), i)) ;
|
|
m_apFilters[i]->Release() ;
|
|
m_apFilters[i] = NULL ;
|
|
}
|
|
if (m_alpszName[i])
|
|
{
|
|
delete m_alpszName[i] ;
|
|
m_alpszName[i] = NULL ;
|
|
}
|
|
if (m_apClsid[i])
|
|
{
|
|
delete m_apClsid[i] ;
|
|
m_apClsid[i] = NULL ;
|
|
}
|
|
m_abIsHW[i] = FALSE ;
|
|
}
|
|
|
|
if(pGraph) {
|
|
pGraph->Release();
|
|
}
|
|
|
|
m_iCount = 0 ;
|
|
m_iHWCount = 0 ;
|
|
}
|
|
|
|
|
|
void CListDecoders::FreeAllMem(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListDecoders::FreeAllMem()"))) ;
|
|
|
|
for (int i = 0 ; i < m_iCount ; i++)
|
|
{
|
|
if (m_alpszName[i])
|
|
{
|
|
delete m_alpszName[i] ;
|
|
m_alpszName[i] = NULL ;
|
|
}
|
|
if (m_apClsid[i])
|
|
{
|
|
delete m_apClsid[i] ;
|
|
m_apClsid[i] = NULL ;
|
|
}
|
|
m_abIsHW[i] = FALSE ;
|
|
}
|
|
m_iCount = 0 ;
|
|
m_iHWCount = 0 ;
|
|
}
|
|
|
|
BOOL CListDecoders::AddFilter(IBaseFilter *pFilter, LPTSTR lpszName, BOOL bHW, GUID *pClsid)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListDecoders::AddFilter(0x%lx, %s, %d, 0x%lx)"),
|
|
pFilter, lpszName, bHW, pClsid)) ;
|
|
|
|
if (m_iCount >= DECLIST_MAX)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("INTERNAL ERROR: Too many filters added to CListDecoders"))) ;
|
|
return FALSE ;
|
|
}
|
|
if (!bHW && NULL == pClsid)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("INTERNAL ERROR: NULL Clsid spcified for HW decoder"))) ;
|
|
return FALSE ;
|
|
}
|
|
|
|
m_alpszName[m_iCount] = new TCHAR [lstrlen(lpszName) + 1] ;
|
|
if (NULL == m_alpszName[m_iCount])
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: CListDecoders::AddFilter() -- Out of memory for filter name"))) ;
|
|
return FALSE ;
|
|
}
|
|
lstrcpy(m_alpszName[m_iCount], lpszName) ;
|
|
//
|
|
// We store Clsid ONLY for SW decs (HW decs get filter pointer thru' DevEnum and we
|
|
// don't instantiate them, so we don't bother about it)
|
|
//
|
|
if (!bHW) // SW dec
|
|
{
|
|
m_apClsid[m_iCount] = (GUID *) new BYTE[sizeof(GUID)] ;
|
|
if (NULL == m_apClsid[m_iCount])
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: CListDecoders::AddFilter() -- Out of memory for Clsid"))) ;
|
|
delete m_alpszName[m_iCount] ; // free the name string too
|
|
return FALSE ;
|
|
}
|
|
*m_apClsid[m_iCount] = *pClsid ;
|
|
}
|
|
else
|
|
m_apClsid[m_iCount] = NULL ;
|
|
|
|
ASSERT(pFilter) ;
|
|
m_apFilters[m_iCount] = pFilter ;
|
|
m_abIsHW[m_iCount] = bHW ;
|
|
m_iCount++ ;
|
|
if (bHW)
|
|
m_iHWCount++ ;
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CListDecoders::GetFilter(int i, IBaseFilter **ppFilter, LPTSTR *lpszName, BOOL *pbHW)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListDecoders::GetFilter(%d, 0x%lx, 0x%lx, %d)"),
|
|
i, ppFilter, lpszName, pbHW)) ;
|
|
|
|
if (i > m_iCount)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("INTERNAL ERROR: Bad index (%d) for CListDecoders::GetFilter()"), i)) ;
|
|
*ppFilter = NULL ;
|
|
*pbHW = FALSE ;
|
|
return FALSE ;
|
|
}
|
|
|
|
*ppFilter = m_apFilters[i] ;
|
|
*lpszName = m_alpszName[i] ;
|
|
*pbHW = m_abIsHW[i] ;
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
int CListDecoders::GetList(IBaseFilter **ppFilter)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListDecoders::GetList(0x%lx)"), ppFilter)) ;
|
|
|
|
if (0 == m_iCount)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Empty decoder list"))) ;
|
|
*ppFilter = NULL ;
|
|
return 0 ;
|
|
}
|
|
*ppFilter = (IBaseFilter *) CoTaskMemAlloc(m_iCount * sizeof(IBaseFilter *)) ;
|
|
if (NULL == *ppFilter)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Out of memory for decoder list"))) ;
|
|
return 0 ;
|
|
}
|
|
for (int i = 0 ; i < m_iCount ; i++)
|
|
ppFilter[i] = m_apFilters[i] ;
|
|
|
|
return m_iCount ;
|
|
}
|
|
|
|
|
|
int CListDecoders::IsInList(BOOL bHW, LPVOID pDec)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListDecoders::IsInList(%d, 0x%lx)"), bHW, pDec)) ;
|
|
|
|
GUID *pClsid ;
|
|
IBaseFilter *pFilter ;
|
|
if (bHW)
|
|
pFilter = (IBaseFilter *) pDec ;
|
|
else
|
|
pClsid = (GUID *) pDec ;
|
|
|
|
for (int i = 0 ; i < m_iCount ; i++)
|
|
{
|
|
if (bHW != m_abIsHW[i]) // dec type mismatch...
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Got a %s decoder looking for a %s decoder (i = %d)"),
|
|
m_abIsHW[i] ? TEXT("HW") : TEXT("SW"),
|
|
bHW ? TEXT("HW") : TEXT("SW"), i)) ;
|
|
continue ; // ...can't be this one
|
|
}
|
|
|
|
if (bHW)
|
|
{
|
|
if (pFilter == m_apFilters[i])
|
|
return i ;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(m_apClsid[i]) ;
|
|
if (*pClsid == *m_apClsid[i])
|
|
return i ;
|
|
}
|
|
}
|
|
|
|
return DECLIST_NOTFOUND ; // didn't match any
|
|
}
|
|
|
|
|
|
//
|
|
// Implementation of the CListIntFilters class...
|
|
//
|
|
CListIntFilters::CListIntFilters(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListIntFilters::CListIntFilters()"))) ;
|
|
|
|
CleanAll() ; // start clean
|
|
}
|
|
|
|
|
|
CListIntFilters::~CListIntFilters(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListIntFilters::~CListIntFilters()"))) ;
|
|
|
|
RemoveAll() ; // make sure no such filters remains in graph
|
|
}
|
|
|
|
|
|
void CListIntFilters::CleanAll(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListIntFilters::CleanAll()"))) ;
|
|
|
|
for (int i = 0 ; i < MAX_INT_FILTERS ; i++)
|
|
{
|
|
m_apFilters[i] = NULL ;
|
|
m_aNumInPins[i] = 0 ;
|
|
m_aNumOutPins[i] = 0 ;
|
|
}
|
|
m_iCount = 0 ;
|
|
}
|
|
|
|
|
|
void CListIntFilters::RemoveAll(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListIntFilters::RemoveAll()"))) ;
|
|
|
|
// Don't have a pointer to the graph, so cache it here from the
|
|
// the first filter.
|
|
IFilterGraph *pGraph = NULL ;
|
|
|
|
for (int i = 0 ; i < m_iCount ; i++)
|
|
{
|
|
if (m_apFilters[i])
|
|
{
|
|
#ifdef DEBUG
|
|
FILTER_INFO fi ;
|
|
m_apFilters[i]->QueryFilterInfo(&fi) ;
|
|
fi.pGraph->Release() ;
|
|
TCHAR achName[MAX_FILTER_NAME] ;
|
|
#ifdef UNICODE
|
|
lstrcpy(achName, fi.achName) ;
|
|
#else
|
|
WideCharToMultiByte(CP_ACP, 0, fi.achName, -1,
|
|
achName, MAX_FILTER_NAME, NULL, NULL) ;
|
|
#endif // UNICODE
|
|
DbgLog((LOG_TRACE, 5, TEXT("Intermediate filter #%d: <%s>"), i, achName)) ;
|
|
#endif // DEBUG
|
|
if (NULL == pGraph)
|
|
{
|
|
FILTER_INFO fi ;
|
|
HRESULT hr = m_apFilters[i]->QueryFilterInfo(&fi) ;
|
|
pGraph = fi.pGraph ;
|
|
|
|
ASSERT(pGraph || FAILED(hr)) ; // filter not on our list if not in graph
|
|
}
|
|
if (pGraph)
|
|
EXECUTE_ASSERT(SUCCEEDED(pGraph->RemoveFilter(m_apFilters[i]))) ;
|
|
|
|
DbgLog((LOG_TRACE, 5, TEXT("Going to release intermediate filter #%d"), i)) ;
|
|
m_apFilters[i]->Release() ;
|
|
m_apFilters[i] = NULL ;
|
|
m_aNumInPins[i] = 0 ;
|
|
m_aNumOutPins[i] = 0 ;
|
|
}
|
|
}
|
|
if (pGraph)
|
|
pGraph->Release() ;
|
|
|
|
m_iCount = 0 ;
|
|
}
|
|
|
|
|
|
BOOL CListIntFilters::IsInList(IBaseFilter *pFilter)
|
|
{
|
|
if (NULL == pFilter)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Null filter pointer passed to CListIntFilters::IsInList()"))) ;
|
|
ASSERT(pFilter) ;
|
|
return FALSE ;
|
|
}
|
|
|
|
for (int i = 0 ; i < m_iCount ; i++)
|
|
{
|
|
if (IsEqualObject(pFilter, m_apFilters[i]))
|
|
return TRUE ; // got a match
|
|
}
|
|
return FALSE ; // no match ==> not in list
|
|
}
|
|
|
|
|
|
BOOL CListIntFilters::AddFilter(IBaseFilter *pFilter)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListIntFilters::AddFilter(0x%lx)"), pFilter)) ;
|
|
|
|
if (NULL == pFilter)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Null filter pointer passed to CListIntFilters::AddFilter()"))) ;
|
|
ASSERT(pFilter) ;
|
|
return FALSE ;
|
|
}
|
|
|
|
if (m_iCount >= MAX_INT_FILTERS)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("INTERNAL ERROR: Too many filters added to CListIntFilters"))) ;
|
|
ASSERT(m_iCount < MAX_INT_FILTERS) ;
|
|
return FALSE ;
|
|
}
|
|
|
|
// Check if this filter is already in our list -- if so, just return
|
|
for (int i = 0 ; i < m_iCount ; i++)
|
|
{
|
|
if (IsEqualObject(pFilter, m_apFilters[i]))
|
|
return TRUE ; // we are done
|
|
}
|
|
|
|
// It's NOT already in our list -- add it now
|
|
m_apFilters[m_iCount] = pFilter ;
|
|
|
|
//
|
|
// Now count the # in and out pins of the intermediate filter
|
|
//
|
|
IPin *pPin ;
|
|
ULONG ul ;
|
|
PIN_DIRECTION pd ;
|
|
IEnumPins *pEnumPin ;
|
|
pFilter->EnumPins(&pEnumPin) ;
|
|
ASSERT(pEnumPin) ;
|
|
if (pEnumPin)
|
|
{
|
|
while (S_OK == pEnumPin->Next(1, &pPin, &ul) && 1 == ul)
|
|
{
|
|
pPin->QueryDirection(&pd) ;
|
|
if (PINDIR_INPUT == pd)
|
|
m_aNumInPins[m_iCount]++ ;
|
|
else if (PINDIR_OUTPUT == pd)
|
|
m_aNumOutPins[m_iCount]++ ;
|
|
else
|
|
ASSERT(PINDIR_INPUT == pd || PINDIR_OUTPUT == pd) ;
|
|
|
|
pPin->Release() ; // done with pin
|
|
}
|
|
pEnumPin->Release() ; // done Enum-ing
|
|
}
|
|
DbgLog((LOG_TRACE, 5, TEXT("CListIntFilters::AddFilter() -- %d in, %d out pin"),
|
|
m_aNumInPins[m_iCount], m_aNumOutPins[m_iCount])) ;
|
|
|
|
m_iCount++ ; // increment counter now
|
|
|
|
return TRUE ; // added to list
|
|
}
|
|
|
|
|
|
int CListIntFilters::GetNumInPin(IBaseFilter *pFilter)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListIntFilters::GetNumInPin(0x%lx)"), pFilter)) ;
|
|
|
|
if (NULL == pFilter)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Null filter pointer passed to CListIntFilters::GetNumInPin()"))) ;
|
|
ASSERT(pFilter) ;
|
|
return 0 ;
|
|
}
|
|
|
|
// Check if this filter is already in our list -- if so, just return
|
|
for (int i = 0 ; i < m_iCount ; i++)
|
|
{
|
|
if (IsEqualObject(pFilter, m_apFilters[i]))
|
|
return m_aNumInPins[i] ;
|
|
}
|
|
|
|
return 0 ; // no match!!!
|
|
}
|
|
|
|
|
|
int CListIntFilters::GetNumOutPin(IBaseFilter *pFilter)
|
|
{
|
|
DbgLog((LOG_TRACE, 4, TEXT("CListIntFilters::GetNumOutPin(0x%lx)"), pFilter)) ;
|
|
|
|
if (NULL == pFilter)
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Null filter pointer passed to CListIntFilters::GetNumOutPin()"))) ;
|
|
ASSERT(pFilter) ;
|
|
return 0 ;
|
|
}
|
|
|
|
// Check if this filter is already in our list -- if so, just return
|
|
for (int i = 0 ; i < m_iCount ; i++)
|
|
{
|
|
if (IsEqualObject(pFilter, m_apFilters[i]))
|
|
return m_aNumOutPins[i] ;
|
|
}
|
|
|
|
return 0 ; // no match!!!
|
|
}
|