Leaked source code of windows server 2003
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.
 
 
 
 
 
 

3869 lines
138 KiB

// Copyright (c) 1994 - 2000 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"
#include "..\image2\inc\vmrp.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_pMapper(NULL),
m_ListFilters(20, 10),
m_ListHWDecs(10, 10),
m_pDVDNav(NULL),
m_pOvM(NULL),
m_pAR(NULL),
m_pVR(NULL),
m_pVMR(NULL),
m_pVPM(NULL),
m_pL21Dec(NULL),
m_bGraphDone(FALSE),
m_bUseVPE(TRUE),
m_bPinNotRendered(FALSE),
m_bDDrawExclMode(FALSE),
m_bTryVMR(TRUE)
{
DbgLog((LOG_TRACE, 3, TEXT("CDvdGraphBuilder::CDvdGraphBuilder()"))) ;
*phr = CreateGraph() ;
}
CDvdGraphBuilder::~CDvdGraphBuilder(void)
{
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() ;
// Remove and release OverlayMixer now, if it was there
if (m_pOvM)
{
EXECUTE_ASSERT(SUCCEEDED(m_pGB->RemoveFilter(m_pOvM))) ;
m_pOvM->Release() ;
m_pOvM = NULL ;
}
// Remove and release VMR, if it was there
if (m_pVMR)
{
EXECUTE_ASSERT(SUCCEEDED(m_pGB->RemoveFilter(m_pVMR))) ;
m_pVMR->Release() ;
m_pVMR = NULL ;
}
m_pGB->Release() ; // free it
m_pGB = NULL ;
}
DbgLog((LOG_TRACE, 3, TEXT("CDvdGraphBuilder::~CDvdGraphBuilder() ending"))) ;
}
// 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 II"), pUnk, phr) ;
}
STDMETHODIMP CDvdGraphBuilder::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
DbgLog((LOG_TRACE, 3, TEXT("CDvdGraphBuilder::NonDelegatingQueryInterface()"))) ;
if (ppv)
*ppv = NULL;
if (riid == IID_IDvdGraphBuilder)
{
DbgLog((LOG_TRACE, 5, TEXT("QI for IDvdGraphBuilder"))) ;
return GetInterface((IDvdGraphBuilder *) this, ppv) ;
}
else // more interfaces
{
return CUnknown::NonDelegatingQueryInterface(riid, ppv) ;
}
}
// -----------------------------
// IDvdGraphBuilder stuff ....
// -----------------------------
//
// 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 ;
EnsureGraphExists() ;
*ppGB = m_pGB ;
if (NULL == m_pGB)
{
return E_UNEXPECTED ;
}
m_pGB->AddRef() ; // app owns a copy now
return NOERROR ;
}
DEFINE_GUID(IID_IDDrawNonExclModeVideo,
0xec70205c, 0x45a3, 0x4400, 0xa3, 0x65, 0xc4, 0x47, 0x65, 0x78, 0x45, 0xc7) ;
DEFINE_GUID(IID_IAMSpecifyDDrawConnectionDevice,
0xc5265dba, 0x3de3, 0x4919, 0x94, 0x0b, 0x5a, 0xc6, 0x61, 0xc8, 0x2e, 0xf4) ;
//
// 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)"),
(LPCTSTR)CDisp(riid), ppvIF)) ;
HRESULT hr ;
if (IsBadWritePtr(ppvIF, sizeof(LPVOID)))
return E_INVALIDARG ;
*ppvIF = NULL ;
// We should be able to provide the IDDrawExclModeVideo interface even
// before the graph is built so that apps can specify their own DDraw
// params to be used by OvMixer to build the graph.
if (IID_IDDrawExclModeVideo == riid ||
IID_IDDrawNonExclModeVideo == riid ||
IID_IAMSpecifyDDrawConnectionDevice == riid)
{
if (NULL == m_pVMR) // if we are already NOT using VMR
{
hr = EnsureOverlayMixerExists() ;
ASSERT(SUCCEEDED(hr) && m_pOvM) ;
if (SUCCEEDED(hr) && m_pOvM)
{
SetVMRUse(FALSE) ; // can't use VMR anymore
return m_pOvM->QueryInterface(riid, (LPVOID *)ppvIF) ;
}
}
return E_NOINTERFACE ;
}
// We should be able to provide the IVMR* interfaces even before the graph
// is built so that apps can specify their own rendering settings to be
// used by VMR whille building the graph.
if (IID_IVMRMixerBitmap == riid ||
IID_IVMRFilterConfig == riid ||
IID_IVMRWindowlessControl == riid ||
IID_IVMRMonitorConfig == riid)
{
if (NULL == m_pOvM) // if we are already NOT using OvMixer
{
hr = EnsureVMRExists() ;
ASSERT(SUCCEEDED(hr) && m_pVMR) ;
if (SUCCEEDED(hr) && m_pVMR)
{
// SetVMRUse(TRUE) ; // should try to use VMR for sure
return m_pVMR->QueryInterface(riid, (LPVOID *)ppvIF) ;
}
}
return E_NOINTERFACE ;
}
// We don't return IVMRPinConfig pointer. If needed the app can get the
// VMR interface and get the pin config interface for the needed pin.
// We can't return ANY OTHER internal filter interface pointers before
// building the whole graph.
if (! m_bGraphDone )
return VFW_E_DVD_GRAPHNOTREADY ;
if (IID_IDvdControl == riid)
{
return m_pDVDNav->QueryInterface(IID_IDvdControl, (LPVOID *)ppvIF) ;
}
else if (IID_IDvdControl2 == riid)
{
return m_pDVDNav->QueryInterface(IID_IDvdControl2, (LPVOID *)ppvIF) ;
}
else if (IID_IDvdInfo == riid)
{
return m_pDVDNav->QueryInterface(IID_IDvdInfo, (LPVOID *)ppvIF) ;
}
else if (IID_IDvdInfo2 == riid)
{
return m_pDVDNav->QueryInterface(IID_IDvdInfo2, (LPVOID *)ppvIF) ;
}
else if (IID_IVideoWindow == riid)
{
if (m_pVR || m_pVMR)
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 if (m_pVMR)
return m_pVMR->QueryInterface(IID_IBasicVideo, (LPVOID *)ppvIF) ;
else
return E_NOINTERFACE ;
}
else if (IID_IBasicAudio == riid)
{
return m_pGB->QueryInterface(IID_IBasicAudio, (LPVOID *)ppvIF) ;
}
else if (IID_IAMLine21Decoder == riid)
{
if (m_pL21Dec)
return m_pL21Dec->QueryInterface(IID_IAMLine21Decoder, (LPVOID *)ppvIF) ;
else
return E_NOINTERFACE ;
}
else if (IID_IMixerPinConfig == riid || IID_IMixerPinConfig2 == riid)
{
// First check if VMR is already being used. In that case we don't use
// OvMixer, and hence no such interface.
if (m_pVMR)
{
DbgLog((LOG_TRACE, 3, TEXT("VMR being used. Can't get IMixerPinConfig(2)."))) ;
return E_NOINTERFACE ;
}
// In all likelihood, this app wants to use the OvMixer. So we'll go on
// that path (create OvMixer, if it's not there) and return the interface.
*ppvIF = NULL ; // initially
hr = EnsureOverlayMixerExists() ;
ASSERT(SUCCEEDED(hr) && m_pOvM) ;
if (SUCCEEDED(hr) && m_pOvM)
{
IEnumPins *pEnumPins ;
IPin *pPin = NULL ;
PIN_DIRECTION pd ;
ULONG ul ;
hr = m_pOvM->EnumPins(&pEnumPins) ;
ASSERT(SUCCEEDED(hr) && pEnumPins) ;
// Get the 1st input pin
while (S_OK == pEnumPins->Next(1, &pPin, &ul) && 1 == ul)
{
pPin->QueryDirection(&pd) ;
if (PINDIR_INPUT == pd)
{
hr = pPin->QueryInterface(riid, (LPVOID *)ppvIF) ;
pPin->Release() ;
break ; // we got it
}
pPin->Release() ;
}
pEnumPins->Release() ; // release before returning
if (*ppvIF)
return S_OK ;
}
return E_NOINTERFACE ;
}
else
return E_NOINTERFACE ;
}
//
// 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 ;
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 VFW_E_DVD_RENDERFAIL ;
}
if (m_bGraphDone) // if graph was built before,
StopGraph() ; // just make sure the graph is in Stopped state first
ClearGraph() ;
m_bPinNotRendered = FALSE ; // reset the flag
ZeroMemory(pStatus, sizeof(AM_DVD_RENDERSTATUS)) ; // clear status
m_bUseVPE = (0 == (dwFlags & AM_DVD_NOVPE)) ; // is VPE needed?
DbgLog((LOG_TRACE, 3, TEXT("Flag: VPE is '%s'"), m_bUseVPE ? "On" : "Off")) ;
dwFlags &= DVDGRAPH_FLAGSVALIDDEC ; // mask off the VPE flag now
if (0 == dwFlags) // 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
}
if (AM_DVD_HWDEC_PREFER != dwFlags && AM_DVD_HWDEC_ONLY != dwFlags &&
AM_DVD_SWDEC_PREFER != dwFlags && AM_DVD_SWDEC_ONLY != dwFlags)
{
DbgLog((LOG_TRACE, 3, TEXT("Invalid dwFlags (0x%lx) specified "), dwFlags)) ;
return E_INVALIDARG ;
}
HRESULT hrFinal = S_OK ;
m_ListFilters.SetGraph(m_pGB) ; // specify graph in which all filters will be added
CheckDDrawExclMode() ; // check if we are building for DDraw exclusive mode
// If we are in DDraw (non-)exclusive mode, we are supposed to use only
// the OvMixer, and not the VMR. We update the flag here and check it in
// the stream render functions.
SetVMRUse(GetVMRUse() && !IsDDrawExclMode()) ;
//
// Instantiate DVD Nav filter first
//
hr = CreateFilterInGraph(CLSID_DVDNavigator, L"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 ;
}
//
// If .._SWDEC_ONLY flag was NOT specified, instantiate all the useful HW
// decoders and maintain a list.
//
if (AM_DVD_SWDEC_ONLY != dwFlags)
{
DbgLog((LOG_TRACE, 5, TEXT(".._SWDEC_ONLY flag has NOT been specified. Enum-ing HW dec filters..."))) ;
hr = CreateDVDHWDecoders() ;
if (FAILED(hr))
{
DbgLog((LOG_TRACE, 5, TEXT("HW DVD decoder enumeration failed (Error 0x%lx)"), hr)) ;
}
}
// Create filter mapper here to use it in the following calls
hr = CoCreateInstance(CLSID_FilterMapper, NULL, CLSCTX_INPROC,
IID_IFilterMapper, (LPVOID *)&m_pMapper) ;
ASSERT(SUCCEEDED(hr) && m_pMapper) ;
// First render the video stream
hr = RenderNavVideoOutPin(dwFlags, pStatus) ;
if (S_OK != hr) // everything isn't good
{
//
// Video stream rendering also includes line21 rendering. If that
// fails due to any reason, including the reason that video decoder
// doesn't have a line21 output pin, we don't want to mark it as a
// video stream rendering failure. The line21 rendering failure
// flags are set deep inside. We set the video decode/render failure
// flags also in the video decode/rendering code. We just downgrade
// the overall result here.
//
DbgLog((LOG_TRACE, 3, TEXT("Something wrong with video stream rendering"))) ;
if (SUCCEEDED(hrFinal)) // was perfect so far
{
DbgLog((LOG_TRACE, 3, TEXT("Overall result downgraded from 0x%lx to 0x%lx"), hrFinal, hr)) ;
hrFinal = hr ;
}
}
// Then render the subpicture stream
hr = RenderNavSubpicOutPin(dwFlags, pStatus) ;
if (S_OK != hr)
{
pStatus->iNumStreamsFailed++ ;
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_SUBPIC ;
if (SUCCEEDED(hrFinal)) // was perfect so far
{
DbgLog((LOG_TRACE, 3, TEXT("Overall result downgraded from 0x%lx to 0x%lx"), hrFinal, hr)) ;
hrFinal = hr ;
}
}
// And then render the audio stream
hr = RenderNavAudioOutPin(dwFlags, pStatus) ;
if (S_OK != hr)
{
pStatus->iNumStreamsFailed++ ;
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_AUDIO ;
if (SUCCEEDED(hrFinal)) // was perfect so far
{
DbgLog((LOG_TRACE, 3, TEXT("Overall result downgraded from 0x%lx to 0x%lx"), hrFinal, hr)) ;
hrFinal = hr ;
}
}
DbgLog((LOG_TRACE, 5, TEXT("Setting number of DVD streams to 3"))) ;
pStatus->iNumStreams = 3 ; // so far 3 DVD streams
//
// In case any output pin was not rendered because we had more than one decoded
// output pin for one stream, we try to locate that pin and render it as a last
// ditch effort.
//
if (m_bPinNotRendered)
{
hr = RenderRemainingPins() ;
if (S_OK != hr) // some problem in rendering
{
if (SUCCEEDED(hrFinal)) // was perfect so far
{
DbgLog((LOG_TRACE, 3, TEXT("Overall result downgraded from 0x%lx to 0x%lx"), hrFinal, hr)) ;
hrFinal = hr ;
}
}
}
//
// Now render any additional streams, e.g, the ASF stream, if any.
//
// Currently does NOT do anything.
//
hr = RenderNavASFOutPin(dwFlags, pStatus) ;
ASSERT(SUCCEEDED(hr)) ;
hr = RenderNavOtherOutPin(dwFlags, pStatus) ;
ASSERT(SUCCEEDED(hr)) ;
// Done with the filter mapper. Let it go now.
m_pMapper->Release() ;
m_pMapper = NULL ;
m_ListHWDecs.ClearList() ; // don't need the extra HW filters anymore
if (pStatus->iNumStreamsFailed >= pStatus->iNumStreams)
{
DbgLog((LOG_TRACE, 1, TEXT("Failed to render %d out of %d main DVD streams (Error 0x%lx)"),
pStatus->iNumStreamsFailed, pStatus->iNumStreams, hrFinal)) ;
return VFW_E_DVD_DECNOTENOUGH; // VFW_E_DVD_RENDERFAIL ;
}
if (FAILED(hrFinal))
{
DbgLog((LOG_TRACE, 1, TEXT("DVD graph building failed with error 0x%lx"),
hrFinal)) ;
return VFW_E_DVD_RENDERFAIL ;
}
//
// Set the specified root file name/DVD volume name (even NULL because
// that causes the DVD Nav to search for one)
//
IDvdControl *pDvdC ;
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 SetRoot() function handle the NULL properly?
//
hr = pDvdC->SetRoot(lpcwszPathName) ;
if (FAILED(hr))
{
DbgLog((LOG_TRACE, 2,
TEXT("IDvdControl::SetRoot(%S) call couldn't use specified volume (Error 0x%lx)"),
lpcwszPathName ? L"NULL" : lpcwszPathName, 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 ;
m_bPinNotRendered = FALSE ; // should reset on success too
return hrFinal ;
}
// private: internal helper methods
//
// 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) ;
}
//
// Make sure OverlayMixer has been created; if not create one here.
//
HRESULT CDvdGraphBuilder::EnsureOverlayMixerExists(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::EnsureOverlayMixerExists()"))) ;
if (m_pOvM)
return S_OK ;
return CreateFilterInGraph(CLSID_OverlayMixer, L"Overlay Mixer", &m_pOvM) ;
}
//
// Make sure VMR has already been created; if not create one here.
//
HRESULT CDvdGraphBuilder::EnsureVMRExists(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::EnsureVMRExists()"))) ;
if (m_pVMR)
return S_OK ;
HRESULT hr ;
hr = CreateFilterInGraph(CLSID_VideoMixingRenderer, L"Video Mixing Renderer", &m_pVMR) ;
ASSERT(m_pVMR) ;
if (SUCCEEDED(hr))
{
IVMRFilterConfigInternal* pVMRConfigInternal;
hr = m_pVMR->QueryInterface(IID_IVMRFilterConfigInternal, (void **) &pVMRConfigInternal);
if( SUCCEEDED( hr )) {
pVMRConfigInternal->SetAspectRatioModePrivate( VMR_ARMODE_LETTER_BOX );
pVMRConfigInternal->Release();
}
// Create three in pins for VMR
hr = CreateVMRInputPins() ;
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Couldn't ensure VMR's 3 in pins"))) ;
SetVMRUse(FALSE) ; // we shouldn't use VMR as it cannot go into mixing mode.
// Should we return some error code to help the app indicate this to the user??
hr = S_FALSE ; // a little problem at least
}
}
return hr ;
}
#define ATI_VENDOR_CODE 0x1002
#define ATI_RAGE_PRO_DEVICE_CODE 0X4742
#define ATI_RAGE_MOBILITY_DEVICE_CODE 0x4C4D
#define INTEL_VENDOR_CODE 0x8086
#define INTEL_810_DEVICE_CODE_1 0x1132
#define INTEL_810_DEVICE_CODE_2 0x7121
#define INTEL_810_DEVICE_CODE_3 0x7123
#define INTEL_810_DEVICE_CODE_4 0x7125
const GUID OUR_IID_IDirectDraw7 =
{
0x15e65ec0, 0x3b9c, 0x11d2,
{
0xb9, 0x2f, 0x00, 0x60, 0x97, 0x97, 0xea, 0x5b
}
};
HRESULT CheckVGADriverIsVMRFriendly(
IBaseFilter* pVMR
)
{
IVMRMonitorConfig* pMon;
if (S_OK != pVMR->QueryInterface(IID_IVMRMonitorConfig, (LPVOID*)&pMon)) {
return E_FAIL;
}
const DWORD dwMAX_MONITORS = 8;
VMRMONITORINFO mi[dwMAX_MONITORS];
DWORD dwNumMonitors;
//
// Get information about all the monitors in the system.
//
if (S_OK != pMon->GetAvailableMonitors(mi, dwMAX_MONITORS, &dwNumMonitors)) {
pMon->Release();
return E_FAIL;
}
//
// Get the current monitors GUID.
//
VMRGUID gu;
HRESULT hr = pMon->GetMonitor(&gu);
pMon->Release();
if (S_OK != hr) {
return E_FAIL;
}
//
// Search for the current monitor in the array of available monitors
//
VMRMONITORINFO* pmi = &mi[0];
for (DWORD i = 0; i < dwNumMonitors; i++, pmi++) {
if (gu.pGUID == NULL && pmi->guid.pGUID == NULL) {
break;
}
if (gu.pGUID != NULL && pmi->guid.pGUID != NULL) {
if (gu.GUID == pmi->guid.GUID) {
break;
}
}
}
//
// Make sure we found a monitor - we should always find a monitor!
//
if (i == dwNumMonitors) {
return E_FAIL;
}
//
// ATi chip sets that don't work with the VMR for DVD playback.
//
if (pmi->dwVendorId == ATI_VENDOR_CODE)
{
switch(pmi->dwDeviceId) {
case ATI_RAGE_PRO_DEVICE_CODE:
return E_FAIL;
case ATI_RAGE_MOBILITY_DEVICE_CODE:
{
IVMRMixerControl* lpMixControl = NULL;
hr = pVMR->QueryInterface(IID_IVMRMixerControl, (LPVOID*)&lpMixControl);
if (SUCCEEDED(hr)) {
DWORD dw;
hr = lpMixControl->GetMixingPrefs(&dw);
if (SUCCEEDED(hr)) {
dw &= ~ MixerPref_FilteringMask;
dw |= MixerPref_PointFiltering;
hr = lpMixControl->SetMixingPrefs(dw);
}
lpMixControl->Release();
}
}
break;
}
}
//
// Intel chip sets that don't work well with the VMR for DVD playback.
// These chipsets do work but the VMR needs to be configured correctly
// to get the best perf form the chipset.
//
else if (pmi->dwVendorId == INTEL_VENDOR_CODE)
{
switch(pmi->dwDeviceId) {
case INTEL_810_DEVICE_CODE_1:
case INTEL_810_DEVICE_CODE_2:
case INTEL_810_DEVICE_CODE_3:
case INTEL_810_DEVICE_CODE_4:
{
//
// We should check the processor speed before
// using the VMR - we need at least 500MHz for
// good quality playback.
//
IVMRMixerControl* lpMixControl = NULL;
hr = pVMR->QueryInterface(IID_IVMRMixerControl, (LPVOID*)&lpMixControl);
if (SUCCEEDED(hr)) {
DWORD dw;
hr = lpMixControl->GetMixingPrefs(&dw);
if (SUCCEEDED(hr)) {
dw &= ~ MixerPref_RenderTargetMask;
dw |= MixerPref_RenderTargetIntelIMC3;
hr = lpMixControl->SetMixingPrefs(dw);
}
lpMixControl->Release();
}
}
break;
}
}
return S_OK;
}
//
// Make sure VMR has at least 3 in pins.
//
HRESULT CDvdGraphBuilder::CreateVMRInputPins(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::CreateVMRInputPins()"))) ;
if (NULL == m_pVMR)
return E_UNEXPECTED ;
// Create three (3) in pins for the VMR so that it can accommodate video,
// SP and CC streams coming in. By default VMR has only one in pin.
HRESULT hr ;
IVMRFilterConfig *pVMRConfig ;
hr = m_pVMR->QueryInterface(IID_IVMRFilterConfig, (LPVOID *) &pVMRConfig) ;
if (SUCCEEDED(hr))
{
DWORD dwStreams = 0 ;
pVMRConfig->GetNumberOfStreams(&dwStreams) ;
if (dwStreams < 3) // if not enough in pins...
{
hr = pVMRConfig->SetNumberOfStreams(3) ;
if (FAILED(hr))
{
DbgLog((LOG_TRACE, 3, TEXT("Couldn't create 3 in pins for VMR"))) ;
hr = E_FAIL ; // This is possible now. We need to turn off VMR use...
}
}
pVMRConfig->Release() ;
if (SUCCEEDED(hr)) {
hr = CheckVGADriverIsVMRFriendly(m_pVMR);
if (FAILED(hr)) {
DbgLog((LOG_TRACE, 3, TEXT("This VGA driver is not compatible with the VMR"))) ;
hr = E_FAIL ; // This is not possible now. We need to turn off VMR use...
}
}
}
else
{
ASSERT(pVMRConfig) ;
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Couldn't get IVMRFilterConfig from VMR!!!"))) ;
hr = S_FALSE ; // a little problem at least
}
return hr ;
}
//
// 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() ;
m_pGB = NULL ;
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 ;
}
// If by any chance, the filter mapper object remained, delete it now
if (m_pMapper)
{
m_pMapper->Release() ;
m_pMapper = NULL ;
}
#pragma message("WARNING: Should we remove the decoder filters first?")
// Remove all filters in our list from the graph
// m_ListFilters.RemoveAllFromGraph() ;
HRESULT hr ;
IEnumPins *pEnumPins ;
IPin *pPin ;
IPin *pPin2 ;
ULONG ul ;
//
// Remove the filters we know about specifically
//
// We don't want to remove OvMixer -- it may have external DDraw params set.
// Just break the connections.
if (m_pOvM)
{
hr = m_pOvM->EnumPins(&pEnumPins) ;
ASSERT(SUCCEEDED(hr) && pEnumPins) ;
while (S_OK == pEnumPins->Next(1, &pPin, &ul) && 1 == ul)
{
hr = pPin->ConnectedTo(&pPin2) ;
if (SUCCEEDED(hr) && pPin2)
{
hr = m_pGB->Disconnect(pPin) ;
ASSERT(SUCCEEDED(hr)) ;
hr = m_pGB->Disconnect(pPin2) ;
ASSERT(SUCCEEDED(hr)) ;
pPin2->Release() ;
}
pPin->Release() ; // done with this pin
}
pEnumPins->Release() ;
}
if (m_pDVDNav)
{
EXECUTE_ASSERT(SUCCEEDED(m_pGB->RemoveFilter(m_pDVDNav))) ;
m_pDVDNav->Release() ;
m_pDVDNav = NULL ;
}
// We don't want to remove VMR (only), because it might have been instantiated
// for an app when it QI-ed for a VMR interface -- just like OvMixer case.
if (m_pVMR)
{
hr = m_pVMR->EnumPins(&pEnumPins) ;
ASSERT(SUCCEEDED(hr) && pEnumPins) ;
while (S_OK == pEnumPins->Next(1, &pPin, &ul) && 1 == ul)
{
hr = pPin->ConnectedTo(&pPin2) ;
if (SUCCEEDED(hr) && pPin2)
{
hr = m_pGB->Disconnect(pPin) ;
ASSERT(SUCCEEDED(hr)) ;
hr = m_pGB->Disconnect(pPin2) ;
ASSERT(SUCCEEDED(hr)) ;
pPin2->Release() ;
}
pPin->Release() ; // done with this pin
}
pEnumPins->Release() ;
}
if (m_pVPM)
{
EXECUTE_ASSERT(SUCCEEDED(m_pGB->RemoveFilter(m_pVPM))) ;
m_pVPM->Release() ;
m_pVPM = NULL ;
}
if (m_pL21Dec)
{
EXECUTE_ASSERT(SUCCEEDED(m_pGB->RemoveFilter(m_pL21Dec))) ;
m_pL21Dec->Release() ;
m_pL21Dec = NULL ;
}
if (m_pAR)
{
EXECUTE_ASSERT(SUCCEEDED(m_pGB->RemoveFilter(m_pAR))) ;
m_pAR->Release() ;
m_pAR = NULL ;
}
if (m_pVR)
{
EXECUTE_ASSERT(SUCCEEDED(m_pGB->RemoveFilter(m_pVR))) ;
m_pVR->Release() ;
m_pVR = NULL ;
}
// Remove all filters in our list from the graph
m_ListFilters.RemoveAllFromGraph() ;
// Enumerate any remaining filters and remove them -- make sure to skip OvMixer
IEnumFilters *pEnumFilters ;
// ULONG ul ; -- defined at the top
IBaseFilter *pFilter ;
m_pGB->EnumFilters(&pEnumFilters) ;
ASSERT(pEnumFilters) ;
while (S_OK == pEnumFilters->Next(1, &pFilter, &ul) && 1 == ul)
{
if (m_pOvM && IsEqualObject(m_pOvM, pFilter) ||
m_pVMR && IsEqualObject(m_pVMR, pFilter))
{
DbgLog((LOG_TRACE, 3,
TEXT("Got OverlayMixer/VMR through filter enum. Not removing from graph."))) ;
}
else
{
EXECUTE_ASSERT(SUCCEEDED(m_pGB->RemoveFilter(pFilter))) ;
}
pFilter->Release() ; // done with this filter
}
pEnumFilters->Release() ; // done enum-ing
m_bGraphDone = FALSE ; // reset the "graph already built" flag
return NOERROR ;
}
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"))) ;
}
// 5 output pins of decoder of matching type is enough
#define MAX_DEC_OUT_PINS 5
void CDvdGraphBuilder::ResetPinInterface(IPin **apPin, int iCount)
{
for (int i = 0 ; i < iCount ; i++)
apPin[i] = NULL ;
}
void CDvdGraphBuilder::ReleasePinInterface(IPin **apPin)
{
// Done with decoded video pin(s) -- release it/them
int i = 0 ;
while (apPin[i])
{
apPin[i]->Release() ;
i++ ;
}
}
HRESULT CDvdGraphBuilder::RenderNavVideoOutPin(DWORD dwDecFlag, AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderNavVideoOutPin(0x%lx, 0x%lx)"),
dwDecFlag, pStatus)) ;
HRESULT hr ;
IPin *pPin ;
IPin *apPinOutDec[MAX_DEC_OUT_PINS + 1] ; // 1 for terminating NULL
ResetPinInterface(apPinOutDec, NUMELMS(apPinOutDec)) ;
hr = FindMatchingPin(m_pDVDNav, AM_DVD_STREAM_VIDEO, PINDIR_OUTPUT, TRUE, 0, &pPin) ;
if (FAILED(hr) || NULL == pPin)
{
DbgLog((LOG_ERROR, 1, TEXT("No open video output pin found on the DVDNav"))) ;
return VFW_E_DVD_RENDERFAIL ;
}
// The dwDecFlag out param is largely ignored, except being passed as an in
// param to the method RenderDecodedVideo() to indicate if the video is
// decoded in HW, so that VPM is used before VMR.
hr = DecodeDVDStream(pPin, AM_DVD_STREAM_VIDEO, &dwDecFlag, pStatus, apPinOutDec) ;
pPin->Release() ; // release DVDNav's video out pin
if (FAILED(hr)) // couldn't find video decoder
{
DbgLog((LOG_TRACE, 1, TEXT("Could not find a decoder for video stream!!!"))) ;
// For video stream, any decode/rendering problem has to be flagged here
// as we just downgrade the final result in the caller, but not set any
// flag there.
pStatus->iNumStreamsFailed++ ;
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
return S_FALSE ; // just a stream will not be rendered
}
//
// Decoding the video stream succeeded. Now if we got a decoded output pin,
// we need to render that too.
//
HRESULT hrFinal = S_OK ;
if (apPinOutDec[0]) // if video decoding is handled and we got a valid output pin
{
//
// Render the decoded video stream (and line21) ONLY IF the user wants that
//
if (m_bUseVPE)
{
hr = RenderDecodedVideo(apPinOutDec, pStatus, dwDecFlag) ;
//
// If the above rendering attempt is successful then we'll
// try to render the line21 output. If the the video decoder
// doesn't have a video output pin, then there is very little
// chance, well no chance, of having a line21 output.
//
if (SUCCEEDED(hr))
{
//
// The Line21 data comes out of the video decoder filter.
// So get the filter from the above decoded video output
// pin and then get to the line21 output pin.
//
//
// We render the line21 out pin of the video decoder
// ONLY IF we are NOT in DDraw exclusive mode.
//
if (IsDDrawExclMode())
{
DbgLog((LOG_TRACE, 3, TEXT("*** Line21 out pin is not rendered in DDraw excl mode"))) ;
pStatus->bNoLine21In = FALSE ; // no problem with line21
pStatus->bNoLine21Out = FALSE ; // ... ... ... ... ...
}
else // normal mode
{
// Now we are free to render the line21 out pin...
IPin *pPinL21Out ;
PIN_INFO pi ;
hr = apPinOutDec[0]->QueryPinInfo(&pi) ; // the first out pin is fine
ASSERT(SUCCEEDED(hr) && pi.pFilter) ;
hr = FindMatchingPin(pi.pFilter, AM_DVD_STREAM_LINE21,
PINDIR_OUTPUT, TRUE, 0, &pPinL21Out) ;
if (SUCCEEDED(hr) && pPinL21Out)
{
pStatus->bNoLine21In = FALSE ; // there is line21 output pin
hr = RenderLine21Stream(pPinL21Out, pStatus) ;
if (SUCCEEDED(hr))
pStatus->bNoLine21Out = FALSE ; // line21 rendering is OK
else
{
pStatus->bNoLine21Out = TRUE ; // line21 rendering failed
hrFinal = S_FALSE ; // not complete success
}
pPinL21Out->Release() ; // done with line21 pin -- release it now
}
else // video decoder doesn't have line21 output at all
{
DbgLog((LOG_TRACE, 3, TEXT("No line21 output pin on the video decoder."))) ;
pStatus->bNoLine21In = TRUE ; // no line21 data from video decoder
hrFinal = S_FALSE ; // not complete success
}
pi.pFilter->Release() ; // otherwise we'll leak it
}
} // end of if (SUCCEEDED(hr))
else
{
DbgLog((LOG_TRACE, 3, TEXT("Rendering video stream failed (Error 0x%lx)"), hr)) ;
hrFinal = S_FALSE ; // major problem -- video stream failed to render
}
} // end of if (m_bUseVPE)
else
{
DbgLog((LOG_TRACE, 3, TEXT("Video Stream: RenderDvdVideoVolume() was called with no VPE flag"))) ;
}
ReleasePinInterface(apPinOutDec) ; // done with decoded video pin(s) -- release it
}
return hrFinal ;
}
HRESULT CDvdGraphBuilder::RenderNavAudioOutPin(DWORD dwDecFlag, AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderNavAudioOutPin(0x%lx, 0x%lx)"),
dwDecFlag, pStatus)) ;
HRESULT hr ;
IPin *pPin ;
IPin *apPinOutDec[MAX_DEC_OUT_PINS + 1] ; // 1 for terminating NULL
ResetPinInterface(apPinOutDec, NUMELMS(apPinOutDec)) ;
hr = FindMatchingPin(m_pDVDNav, AM_DVD_STREAM_AUDIO, PINDIR_OUTPUT, TRUE, 0, &pPin) ;
if (FAILED(hr) || NULL == pPin)
{
DbgLog((LOG_ERROR, 1, TEXT("No audio output pin found on the DVDNav"))) ;
return VFW_E_DVD_RENDERFAIL ;
}
hr = DecodeDVDStream(pPin, AM_DVD_STREAM_AUDIO, &dwDecFlag, // we ignore returned dwDecFlag here
pStatus, apPinOutDec) ;
pPin->Release() ; // release DVDNav's audio out pin
if (FAILED(hr)) // couldn't find audio decoder
{
DbgLog((LOG_TRACE, 1, TEXT("Could not find a decoder for audio stream!!!"))) ;
return S_FALSE ; // just a stream will not be rendered
}
//
// Decoding the audio stream succeeded. Now if we got a decoded output pin,
// we need to render that too.
//
if (apPinOutDec[0]) // if audio decoding is handled and we got a valid output pin
{
hr = RenderDecodedAudio(apPinOutDec, pStatus) ;
if (S_OK != hr)
{
DbgLog((LOG_TRACE, 3, TEXT("Could not render decoded audio stream"))) ;
hr = S_FALSE ; // partial failure to be returned
}
ReleasePinInterface(apPinOutDec) ; // done with decoded audio pin -- release it
}
return hr ;
}
HRESULT CDvdGraphBuilder::RenderNavSubpicOutPin(DWORD dwDecFlag, AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderNavSubpicOutPin(0x%lx, 0x%lx)"),
dwDecFlag, pStatus)) ;
HRESULT hr ;
IPin *pPin ;
IPin *apPinOutDec[MAX_DEC_OUT_PINS + 1] ; // 1 for terminating NULL
ResetPinInterface(apPinOutDec, NUMELMS(apPinOutDec)) ;
hr = FindMatchingPin(m_pDVDNav, AM_DVD_STREAM_SUBPIC, PINDIR_OUTPUT, TRUE, 0, &pPin) ;
if (FAILED(hr) || NULL == pPin)
{
DbgLog((LOG_ERROR, 1, TEXT("No subpicture output pin found on the DVDNav"))) ;
return VFW_E_DVD_RENDERFAIL ;
}
// Pass dwDecFlag as a in/out param to get back what kind of SP decoder was
// actually used. We'll use that to hack below.
hr = DecodeDVDStream(pPin, AM_DVD_STREAM_SUBPIC, &dwDecFlag,
pStatus, apPinOutDec) ;
pPin->Release() ; // release DVDNav's subpic out pin
if (FAILED(hr)) // couldn't find SP decoder
{
DbgLog((LOG_TRACE, 1, TEXT("Could not find a decoder for SP stream!!!"))) ;
return S_FALSE ; // just a stream will not be rendered
}
//
// Decoding the SP stream succeeded. Now if we got a decoded output pin,
// we need to render that too.
//
if (apPinOutDec[0]) // there is a decoded SP out pin
{
hr = RenderDecodedSubpic(apPinOutDec, pStatus) ;
//
// HACK HACK HACK:
// In general HW decoders mix the SP and video in HW rather than popping a
// SP output pin. We may land up getting a (seemingly) video out pin, which
// for SW decoders may mean a decoded SP output pin, but for HW decoders it's
// certainly some other thing (c-cube DVXplorer) and it will not connect to
// OvMixer/VPM+VMR.
// We don't avoid trying to connect such a pin to OvMixer/VPM+VMR (done above),
// but in case it fails (as it is expected to), we just ignore the error and do
// NOT consider it as a SP stream rendering failure.
//
if (AM_DVD_HWDEC_ONLY == dwDecFlag) // here means HW decoder was used for SP
{
DbgLog((LOG_TRACE, 3,
TEXT("SP stream is decoded in HW. We ignore any error in rendering (0x%lx)"),
hr)) ;
hr = S_OK ;
}
else // for SW decoder
{
if (FAILED(hr)) // connection to renderer's in pin failed => no SP
{
DbgLog((LOG_TRACE, 3, TEXT("Decoded SP out pin could NOT connect to renderer"))) ;
// propagate only S_FALSE to the caller
hr = S_FALSE ; // because just a stream is not rendered right
}
}
ReleasePinInterface(apPinOutDec) ; // done with decoded SP pin -- release it
}
return hr ;
}
//
// *** NOT YET IMPLEMENTED ***
//
HRESULT CDvdGraphBuilder::RenderNavASFOutPin(DWORD dwDecFlag, AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderNavASFOutPin(0x%lx, 0x%lx) -- ** Not Implemented **"),
dwDecFlag, pStatus)) ;
return S_OK ;
}
//
// *** NOT YET IMPLEMENTED ***
//
HRESULT CDvdGraphBuilder::RenderNavOtherOutPin(DWORD dwDecFlag, AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderNavOtherOutPin(0x%lx, 0x%lx) -- ** Not Implemented **"),
dwDecFlag, pStatus)) ;
return S_OK ;
}
HRESULT CDvdGraphBuilder::RenderRemainingPins(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderRemainingPins() -- ** Not Implemented **"))) ;
ASSERT(FALSE) ; // so that we know about it
return S_FALSE ; // so that graph building doesn't fail completely
}
HRESULT CDvdGraphBuilder::DecodeDVDStream(IPin *pPinOut, DWORD dwStream, DWORD *pdwDecFlag,
AM_DVD_RENDERSTATUS *pStatus, IPin **apPinOutDec)
{
DbgLog((LOG_TRACE, 4,
TEXT("CDvdGraphBuilder::DecodeDVDStream(%s, 0x%lx, 0x%lx, 0x%lx, 0x%lx)"),
(LPCTSTR)CDisp(pPinOut), dwStream, *pdwDecFlag, pStatus, apPinOutDec)) ;
HRESULT hr ;
IPin *pPinIn ; // the (end) pin we finally connected to
DWORD dwNewDecFlag = *pdwDecFlag ; // let's start with what we have
// ResetPinInterface(apPinOutDec, NUMELMS(apPinOutDec)) ;
//
// We'll note what decoder option we actually use, but will not update the
// value at the passed in pointer until we have checked that the stream has
// really been decoded completely. So the new flag is assigned way below
// when we verify that the output stream gives decoded output.
//
// Also H/SWDecodeDVDStream() methods will try to detect if the video/SP
// decoder is VMR-compatible, and if not, set a flag (m_bTryVMR to FALSE),
// so that RenderDecodedVideo() method can determine which renderer to use.
//
switch (*pdwDecFlag) // based on the user-specified decoding option
{
case AM_DVD_HWDEC_ONLY:
hr = HWDecodeDVDStream(pPinOut, dwStream, &pPinIn, pStatus) ;
if (FAILED(hr))
return hr ;
// *pdwDecFlag = AM_DVD_HWDEC_ONLY ; -- unchanged
break ;
case AM_DVD_HWDEC_PREFER:
hr = HWDecodeDVDStream(pPinOut, dwStream, &pPinIn, pStatus) ;
if (FAILED(hr)) // if didn't succeed, try SW decode too
{
hr = SWDecodeDVDStream(pPinOut, dwStream, &pPinIn, pStatus) ;
if (FAILED(hr)) // now we give up
return hr ;
else
dwNewDecFlag = AM_DVD_SWDEC_ONLY ; // we preferred HW, but did it in SW
}
else
dwNewDecFlag = AM_DVD_HWDEC_ONLY ; // we preferred HW and got HW
break ;
case AM_DVD_SWDEC_ONLY:
hr = SWDecodeDVDStream(pPinOut, dwStream, &pPinIn, pStatus) ;
if (FAILED(hr))
return hr ;
break ;
case AM_DVD_SWDEC_PREFER:
hr = SWDecodeDVDStream(pPinOut, dwStream, &pPinIn, pStatus) ;
if (FAILED(hr)) // if didn't succeed, try SW decode too
{
hr = HWDecodeDVDStream(pPinOut, dwStream, &pPinIn, pStatus) ;
if (FAILED(hr)) // now we give up
return hr ;
else
dwNewDecFlag = AM_DVD_HWDEC_ONLY ; // we preferred SW, but got HW
}
else
dwNewDecFlag = AM_DVD_SWDEC_ONLY ; // we preferred SW and got SW
break ;
default:
DbgLog((LOG_ERROR, 1, TEXT("ERROR: How did dwFlags=0x%lx get passed in?"), *pdwDecFlag)) ;
return E_INVALIDARG ;
} // end of switch(*pdwDecFlag)
//
// Now see if the stream has been completely decoded
//
ASSERT(pPinIn) ; // so that otherwise we know
if (NULL == pPinIn)
{
DbgLog((LOG_ERROR, 1, TEXT("ERROR: How can the connected to pin be NULL after connection?"))) ;
return E_FAIL ;
}
IPin *pPinOut2 ;
PIN_INFO pi ;
pPinIn->QueryPinInfo(&pi) ;
pPinIn->Release() ; // don't need the in pin anymore
DWORD dw ; // temp variable for stream type
int iPos = 0 ; // which instance of pin of the filter
int iCount = 0 ; // how many decoded output pins have we found (expected only 1)
while (SUCCEEDED(hr = FindMatchingPin(pi.pFilter, 0, PINDIR_OUTPUT, TRUE, iPos, &pPinOut2)) &&
NULL != pPinOut2)
{
if (dwStream != (dw = GetPinStreamType(pPinOut2)))
{
//
// Hack: The mediatype for decoded subpicture is video. So while rendering
// the subpicture stream, if we don't find a subpicture out pin, look for a
// video out pin too.
//
if (AM_DVD_STREAM_SUBPIC == dwStream)
{
DbgLog((LOG_TRACE, 3, TEXT("No open out pin for SP stream"))) ;
//
// If the output pin is of type video then it's OK --
// it's the out pin for decoded SP content.
//
if (AM_DVD_STREAM_VIDEO != dw)
{
DbgLog((LOG_TRACE, 3,
TEXT("*** Could NOT find open out pin #%d of type 0x%lx for filter of pin %s (SP) ***"),
iPos, dw, (LPCTSTR)CDisp(pPinIn))) ;
pPinOut2->Release() ; // otherwise we'll leak!!!
iPos++ ;
continue ; // check for other out pins
}
DbgLog((LOG_TRACE, 3, TEXT("Found open video out pin %s for the SP stream"),
(LPCTSTR)CDisp(pPinOut2))) ;
} // end of if (subpic)
else // non-subpicture stream
{
DbgLog((LOG_TRACE, 1,
TEXT("*** Could NOT find open out pin #%d of type 0x%lx for filter of pin %s ***"),
iPos, dw, (LPCTSTR)CDisp(pPinIn))) ;
pPinOut2->Release() ; // otherwise we'll leak!!!
iPos++ ;
continue ; // check for other out pins
}
}
else
DbgLog((LOG_TRACE, 3, TEXT("Found open out pin %s of matching type 0x%lx"),
(LPCTSTR)CDisp(pPinOut2), dwStream)) ;
// Is the output decoded now?
if (IsOutputDecoded(pPinOut2))
{
DbgLog((LOG_TRACE, 1,
TEXT("Pin %s is going to be returned as decoded out pin #%ld of stream type %ld"),
(LPCTSTR)CDisp(pPinOut2), iCount+1, dwStream)) ;
if (iCount < MAX_DEC_OUT_PINS)
{
apPinOutDec[iCount] = pPinOut2 ;
iCount++ ;
//
// This is the right place to update the actually used decoder flag
//
// NOTE: There is this bleak chance of having multiple output pins etc.
// but that's a pathological case and we do this for the SP stream only.
//
if (*pdwDecFlag != dwNewDecFlag)
{
DbgLog((LOG_TRACE, 2,
TEXT("Decoding option changed from 0x%lx to 0x%lx for stream 0x%lx on out pin %s"),
*pdwDecFlag, dwNewDecFlag, dwStream, (LPCTSTR)CDisp(pPinOut2))) ;
*pdwDecFlag = dwNewDecFlag ;
}
}
else
{
DbgLog((LOG_TRACE, 1, TEXT("WARNING: Way too many out pins to be returned. Ignoring now..."))) ;
}
}
else // not yet fully decoded -- try more
{
hr = DecodeDVDStream(pPinOut2, dwStream, pdwDecFlag, pStatus, apPinOutDec) ;
if (FAILED(hr))
{
DbgLog((LOG_TRACE, 3, TEXT("Decoding of pin %s failed (Error 0x%lx)"),
(LPCTSTR)CDisp(pPinOut2), hr)) ;
pPinOut2->Release() ;
pi.pFilter->Release() ; // else we leak!!!
return hr ;
}
pPinOut2->Release() ; // done with this pin
}
iPos++ ; // look for the next open out pin
DbgLog((LOG_TRACE, 5, TEXT("Going to look for open out pin #%d..."), iPos)) ;
} // end of while (FindMatchingPin()) loop
pi.pFilter->Release() ; // else we leak!!!
return S_OK ; // success!!!
}
//
// There is an assumption in this function that we don't need to create multiple
// instances of a WDM filter to get a suitable input pin on it. If we have to
// ever do that there has to be substantial changes in this function and/or
// CreateDVDHWDecoders() function.
//
HRESULT CDvdGraphBuilder::HWDecodeDVDStream(IPin *pPinOut, DWORD dwStream, IPin **ppPinIn,
AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4,
TEXT("CDvdGraphBuilder::HWDecodeDVDStream(%s, 0x%lx, 0x%lx, 0x%lx)"),
(LPCTSTR)CDisp(pPinOut), dwStream, ppPinIn, pStatus)) ;
*ppPinIn = NULL ; // to start with
int iCount = m_ListHWDecs.GetCount() ;
if (0 == iCount)
return VFW_E_DVD_DECNOTENOUGH ;
HRESULT hr ;
BOOL bConnected = FALSE ; // to start with
int i ;
int j ;
BOOL bNewlyAdded ;
LPWSTR lpszwName ;
IBaseFilter *pFilter ;
IPin *pPinIn ;
for (i = 0 ; !bConnected && i < iCount ; i++)
{
// Get the next HW decoder filter
if (! m_ListHWDecs.GetFilter(i, &pFilter, &lpszwName) )
{
DbgLog((LOG_ERROR, 0, TEXT("ERROR: m_ListHWDecs.GetFilter(%d, ...) failed"), i)) ;
ASSERT(FALSE) ; // so we don't ignore itd
break ;
}
DbgLog((LOG_TRACE, 3, TEXT("HW Dec filter %S will be tried."), lpszwName)) ;
// If this HW decoder filter is already not in the graph, add it
if (! m_ListFilters.IsInList(pFilter) )
{
DbgLog((LOG_TRACE, 5, TEXT("Filter %S is NOT already in use"), lpszwName)) ;
hr = m_pGB->AddFilter(pFilter, lpszwName) ;
ASSERT(SUCCEEDED(hr)) ;
bNewlyAdded = TRUE ;
}
else
bNewlyAdded = FALSE ;
// Try every input pin of the required mediatype
j = 0 ;
while ( // !bConnected && -- we 'break' out of this loop on connection
SUCCEEDED(hr = FindMatchingPin(pFilter, dwStream, PINDIR_INPUT,
TRUE, j, &pPinIn)) &&
pPinIn)
{
// We got an input pin of the required mediatype
hr = ConnectPins(pPinOut, pPinIn, AM_DVD_CONNECT_DIRECTFIRST) ;
if (SUCCEEDED(hr))
{
if (bNewlyAdded)
{
DbgLog((LOG_TRACE, 5, TEXT("Filter %S added to list of filters"), lpszwName)) ;
m_ListFilters.AddFilter(pFilter, lpszwName, NULL) ; // add to list
pFilter->AddRef() ; // we need an extra AddRef() here
}
EnumFiltersBetweenPins(dwStream, pPinOut, pPinIn, pStatus) ;
*ppPinIn = pPinIn ; // return this input pin to the caller
bConnected = TRUE ;
break ; // connected -- get out of this loop
// REMEMBER: release the returned pin in the caller
}
pPinIn->Release() ; // done with this in pin
j++ ; // go for the next pin...
} // end of while (!bConnected && FindMatchingPin())
// If we couldn't make any connection in the above while() loop then
// remove the filter, ONLY IF it was added just before the loop.
if (!bConnected && bNewlyAdded)
{
DbgLog((LOG_TRACE, 5,
TEXT("Couldn't connect to newly added filter %S. Removing it."), lpszwName)) ;
hr = m_pGB->RemoveFilter(pFilter) ;
ASSERT(SUCCEEDED(hr)) ;
}
} // end of for (i)
if (! bConnected )
return VFW_E_DVD_DECNOTENOUGH ;
return S_OK ; // success!!
}
HRESULT CDvdGraphBuilder::SWDecodeDVDStream(IPin *pPinOut, DWORD dwStream, IPin **ppPinIn,
AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4,
TEXT("CDvdGraphBuilder::SWDecodeDVDStream(%s, 0x%lx, 0x%lx, 0x%lx)"),
(LPCTSTR)CDisp(pPinOut), dwStream, ppPinIn, pStatus)) ;
HRESULT hr ;
IBaseFilter *pFilter ;
IEnumRegFilters *pEnumFilters ;
REGFILTER *pRegFilter ;
IEnumMediaTypes *pEnumMT ;
AM_MEDIA_TYPE *pmt = NULL;
IPin *pPinIn ;
BOOL bConnected = FALSE ; // to start with
BOOL bNewlyAdded ;
ULONG ul ;
int iPos ;
int j ;
PIN_INFO pi ;
*ppPinIn = NULL ; // to start with
pPinOut->EnumMediaTypes(&pEnumMT) ;
ASSERT(pEnumMT) ;
// HACK (kind of) to avoid the Duck filter getting picked up for the SP decoding.
// First try the existing filters in the graph to see if any of those will take
// this output pin.
hr = pPinOut->QueryPinInfo(&pi) ;
ASSERT(SUCCEEDED(hr)) ;
while (!bConnected &&
S_OK == pEnumMT->Next(1, &pmt, &ul) && 1 == ul)
{
if (pPinIn = GetFilterForMediaType(dwStream, pmt, pi.pFilter))
{
hr = ConnectPins(pPinOut, pPinIn, AM_DVD_CONNECT_DIRECTONLY) ; // .._DIRECTFIRST
if (SUCCEEDED(hr))
{
bConnected = TRUE ;
*ppPinIn = pPinIn ; // return this input pin to the caller
}
else
pPinIn->Release() ; // release interface only if connection failed
}
DeleteMediaType(pmt) ; // done with this mediatype
pmt = NULL;
} // end of while() loop
if (pi.pFilter) // just being cautious
pi.pFilter->Release() ; // release now; else we leak.
if (bConnected) // if succeeded in connecting, we are done here
{
pEnumMT->Release() ; // done with the MT enumerator
return S_OK ; // success!!!
}
//
// This output pin does NOT connect to any of the existing filters in the graph.
// Try to pick one from the regsitry, i.e., the standard process.
//
pEnumMT->Reset() ; // start from the beginning again
while (!bConnected &&
S_OK == pEnumMT->Next(1, &pmt, &ul) && 1 == ul)
{
hr = m_pMapper->EnumMatchingFilters(&pEnumFilters, MERIT_DO_NOT_USE+1,
TRUE, pmt->majortype, pmt->subtype,
FALSE, TRUE, GUID_NULL, GUID_NULL) ;
if (FAILED(hr) || NULL == pEnumFilters)
{
DbgLog((LOG_ERROR, 1, TEXT("ERROR: No matching filter enum found (Error 0x%lx)"), hr)) ;
DeleteMediaType(pmt) ;
return VFW_E_DVD_RENDERFAIL ;
}
while (!bConnected &&
S_OK == pEnumFilters->Next(1, &pRegFilter, &ul) && 1 == ul)
{
bNewlyAdded = FALSE ; // to start the loop with...
iPos = 0 ;
// Until connected and we can locate an existing (in use) filter from our list
while (!bConnected &&
m_ListFilters.GetFilter(&pRegFilter->Clsid, iPos, &pFilter)) // already in use
{
j = 0 ;
while (SUCCEEDED(hr = FindMatchingPin(pFilter, 0, PINDIR_INPUT, TRUE, j, &pPinIn)) &&
pPinIn) // got an(other) open in pin
{
DbgLog((LOG_TRACE, 5, TEXT("Got in pin %s (%d) of filter %S (old). Try to connect..."),
(LPCTSTR)CDisp(pPinIn), j, pRegFilter->Name)) ;
hr = ConnectPins(pPinOut, pPinIn, AM_DVD_CONNECT_DIRECTONLY) ; // .._DIRECTFIRST
if (SUCCEEDED(hr))
{
if (bNewlyAdded)
m_ListFilters.AddFilter(pFilter, NULL, &(pRegFilter->Clsid)) ;
// Don't need AddRef() here as it has just been CoCreateInstance()-ed above
// and is NOT shared between 2 lists.
bConnected = TRUE ;
// pPinIn->Release() ; // done with this pin -- release in the caller
*ppPinIn = pPinIn ; // return this input pin to the caller
break ; // connection happened -- out of this loop
// REMEMBER: Release the returned pin in the caller function
}
else // couldn't connect
{
pPinIn->Release() ; // done with this pin
j++ ; // try next in pin of this filter
}
} // end of while ()
iPos++ ; // for next filter in list
}
if (bConnected) // already succeeded -- we are done!!!
{
CoTaskMemFree(pRegFilter) ;
break ;
}
DbgLog((LOG_TRACE, 5, TEXT("Instance %d of filter %S is being created"),
iPos, pRegFilter->Name)) ;
hr = CreateFilterInGraph(pRegFilter->Clsid, pRegFilter->Name, &pFilter) ;
if (FAILED(hr))
{
DbgLog((LOG_TRACE, 3, TEXT("Failed to create filter %S (Error 0x%lx)"), pRegFilter->Name, hr)) ;
CoTaskMemFree(pRegFilter) ; // release this reg filter's info
continue ; // try the next one
}
bNewlyAdded = TRUE ;
j = 0 ;
while (!bConnected && // not connected AND ...
SUCCEEDED(hr = FindMatchingPin(pFilter, 0, PINDIR_INPUT, TRUE, j, &pPinIn)) &&
pPinIn) // ...got an open in pin
{
DbgLog((LOG_TRACE, 5, TEXT("Got in pin %s (%d) of filter %S (new). Try to connect..."),
(LPCTSTR)CDisp(pPinIn), j, pRegFilter->Name)) ;
hr = ConnectPins(pPinOut, pPinIn, AM_DVD_CONNECT_DIRECTONLY) ; // .._DIRECTFIRST
if (SUCCEEDED(hr))
{
if (bNewlyAdded)
m_ListFilters.AddFilter(pFilter, NULL, &(pRegFilter->Clsid)) ;
// Don't need AddRef() here as it has just been CoCreateInstance()-ed above
// and is NOT shared between 2 lists.
bConnected = TRUE ;
*ppPinIn = pPinIn ; // return this input pin to the caller
// REMEMBER: release the returned pin in the caller function
}
else // couldn't connect
{
pPinIn->Release() ; // done with this pin
j++ ; // try next in pin of this filter
}
// pPinIn->Release() ; // done with this pin
} // end of while (FindMatchingPin())
if (bConnected) // Nav -> Filter (this) succeeded
{
// Video and SP stream: check for VMR compatibility
if (AM_DVD_STREAM_VIDEO == dwStream ||
AM_DVD_STREAM_SUBPIC == dwStream)
{
// Filter, hopefully decoder, has been connected to the Nav.
// Now check if it's VMR ompatible.
BOOL bUseVMR = IsFilterVMRCompatible(pFilter) ;
SetVMRUse(GetVMRUse() && bUseVMR) ;
DbgLog((LOG_TRACE, 3, TEXT("Filter %S is %s VMR compatible"),
pRegFilter->Name, bUseVMR ? TEXT("") : TEXT("*NOT*"))) ;
}
}
else // connection failed
{
// If the failed filter was just added then remove it from
// graph and release it now.
if (bNewlyAdded)
{
DbgLog((LOG_TRACE, 3, TEXT("Couldn't connect to filter %S. Removing it."),
pRegFilter->Name)) ;
m_pGB->RemoveFilter(pFilter) ; // not in this graph
pFilter->Release() ; // don't need this filter
}
}
CoTaskMemFree(pRegFilter) ; // done with this registered filter
} // end of while (!bConnected && pEnumFilters->Next())
pEnumFilters->Release() ; // done with filter enumerator
// release last media type
DeleteMediaType(pmt) ;
pmt = NULL;
} // end of while (enum MTs)
pEnumMT->Release() ; // done with the MT enumerator
if (!bConnected)
return VFW_E_DVD_DECNOTENOUGH ;
return S_OK ; // success!!
}
BOOL CDvdGraphBuilder::IsFilterVMRCompatible(IBaseFilter *pFilter)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::IsFilterVMRCompatible(0x%lx)"), pFilter)) ;
BOOL bResult = FALSE ; // assume old decoder
//
// Updated DVD decoders implement IAMDecoderCaps interface to indicate their
// VMR compatibility.
//
IAMDecoderCaps *pDecCaps ;
HRESULT hr = pFilter->QueryInterface(IID_IAMDecoderCaps, (LPVOID *) &pDecCaps) ;
if (SUCCEEDED(hr))
{
DWORD dwCaps = 0 ;
hr = pDecCaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR_SUPPORT, &dwCaps) ;
if (SUCCEEDED(hr))
{
bResult = (dwCaps & VMR_SUPPORTED) != 0 ;
}
else
DbgLog((LOG_TRACE, 1, TEXT("IAMDecoderCaps::GetDecoderCaps() failed (error 0x%lx)"), hr)) ;
pDecCaps->Release() ; // done with it
}
else
DbgLog((LOG_TRACE, 5, TEXT("(Old) Decoder does NOT support IAMDecoderCaps interface"))) ;
return bResult ;
}
#if 0
void PrintPinRefCount(LPCSTR lpszStr, IPin *pPin)
{
#pragma message("WARNING: Should we remove PrintPinRefCount()?")
#pragma message("WARNING: or at least #ifdef DEBUG?")
pPin->AddRef() ;
LONG l = pPin->Release() ;
DbgLog((LOG_TRACE, 5, TEXT("Ref Count of %s -- %hs: %ld"),
(LPCTSTR) CDisp(pPin), lpszStr, l)) ;
}
#endif // #if 0
HRESULT CDvdGraphBuilder::ConnectPins(IPin *pPinOut, IPin *pPinIn, DWORD dwOption)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::ConnectPins(%s, %s, 0x%lx)"),
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn), dwOption)) ;
// #pragma message("WARNING: Should we remove calls to PrintPinRefCount()?")
// PrintPinRefCount("Before connection", pPinOut) ;
// PrintPinRefCount("Before connection", pPinIn) ;
HRESULT hr ;
switch (dwOption)
{
case AM_DVD_CONNECT_DIRECTONLY:
case AM_DVD_CONNECT_DIRECTFIRST:
hr = m_pGB->ConnectDirect(pPinOut, pPinIn, NULL) ;
if (SUCCEEDED(hr))
{
DbgLog((LOG_TRACE, 3, TEXT("Pin %s *directly* connected to pin %s"),
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
// PrintPinRefCount("After connection", pPinOut) ;
// PrintPinRefCount("After connection", pPinIn) ;
return hr ;
}
else // couldn't connect directly
{
if (AM_DVD_CONNECT_DIRECTONLY == dwOption)
{
// PrintPinRefCount("After connection failed", pPinOut) ;
// PrintPinRefCount("After connection failed", pPinIn) ;
return hr ;
}
// else let it fall through to try indirect connect next
}
case AM_DVD_CONNECT_INDIRECT:
hr = m_pGB->Connect(pPinOut, pPinIn) ;
if (SUCCEEDED(hr))
{
DbgLog((LOG_TRACE, 3, TEXT("Pin %s *indirectly* connected to pin %s"),
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
}
else
{
DbgLog((LOG_TRACE, 5, TEXT("Pin %s did NOT even *indirectly* connect to pin %s"),
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
}
// PrintPinRefCount("After connection attempt", pPinOut) ;
// PrintPinRefCount("After connection attempt", pPinIn) ;
return hr ; // whatever it is
default:
return E_UNEXPECTED ;
}
}
HRESULT CDvdGraphBuilder::RenderVideoUsingOvMixer(IPin **apPinOut,
AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderVideoUsingOvMixer(0x%lx, 0x%lx)"),
apPinOut, pStatus)) ;
HRESULT hr ;
IPin *pPinIn ;
BOOL bConnected = FALSE ; // until connects
//
// If VMR has somehow been instantiated, we need to remove and release it now.
//
if (m_pVMR)
{
DbgLog((LOG_TRACE, 3, TEXT("VMR was somehow created and not in use. Removing it..."))) ;
// Remove it from graph and release it
m_pGB->RemoveFilter(m_pVMR) ;
m_pVMR->Release() ;
m_pVMR = NULL ;
}
// IMPORTANT NOTE:
// For video stream, any decode/rendering problem has to be flagged here
// as we just downgrade the final result in the caller, but not set any
// flag there. Also in RenderDecodedVideo(), we may try to use VMR, and
// if that fails, we fall back on OvMixer. If rendering through OvMixer
// also fails, then only we set the rendering error status and code.
//
hr = EnsureOverlayMixerExists() ;
if (FAILED(hr))
{
// pStatus->hrVPEStatus = hr ; -- actually VPE/Overlay wasn't tried even
DbgLog((LOG_TRACE, 3, TEXT("Overlay Mixer couldn't be started!!!"))) ;
pStatus->iNumStreamsFailed++ ;
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
return VFW_E_DVD_RENDERFAIL ;
}
// Connect given output pin to OverlayMixer's first input pin
hr = FindMatchingPin(m_pOvM, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ;
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 1, TEXT("WARNING: No open input pin found on OverlayMixer (Error 0x%lx)"), hr)) ;
ASSERT(FALSE) ; // so that we know of this weird case
DbgLog((LOG_TRACE, 3, TEXT("No input pin found on Overlay Mixer!!!"))) ;
pStatus->iNumStreamsFailed++ ;
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
return VFW_E_DVD_RENDERFAIL ;
}
int i = 0 ;
while (!bConnected && i < MAX_DEC_OUT_PINS && apPinOut[i])
{
hr = ConnectPins(apPinOut[i], pPinIn, AM_DVD_CONNECT_DIRECTFIRST) ;
if (FAILED(hr))
{
pStatus->hrVPEStatus = hr ;
i++ ;
ASSERT(i <= MAX_DEC_OUT_PINS) ;
}
else
{
bConnected = TRUE ;
pStatus->hrVPEStatus = S_OK ; // make sure we don't return any error code
}
}
pPinIn->Release() ; // done with the pin
if (!bConnected) // if connection to OvMixer's in pin failed => no video on screen
{
DbgLog((LOG_TRACE, 3, TEXT("None of the %d video output pins could be connected to OvMixer"), i)) ;
pStatus->iNumStreamsFailed++ ;
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
return S_FALSE ;
}
// Now see if OverlayMixer has an output pin (no out pin in DDraw excl mode).
// If it has, connect that to the Video Renderer.
IPin *pPinOutOvM ;
hr = FindMatchingPin(m_pOvM, 0, PINDIR_OUTPUT, TRUE, 0, &pPinOutOvM) ;
if (FAILED(hr) || NULL == pPinOutOvM)
{
DbgLog((LOG_TRACE, 1, TEXT("No output pin of OverlayMixer -- in DDraw excl mode?"))) ;
//ASSERT(IsDDrawExclMode()) ;
return S_OK ; // nothing more to do
}
// Create the Video Renderer filter and connect OvMixer's out pin to that
bConnected = FALSE ; // until connected
hr = CreateFilterInGraph(CLSID_VideoRenderer, L"Video Renderer", &m_pVR) ;
if (SUCCEEDED(hr) && m_pVR)
{
hr = FindMatchingPin(m_pVR, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ; // Caution: re-using pPinIn
if (SUCCEEDED(hr) && pPinIn)
{
hr = ConnectPins(pPinOutOvM, pPinIn, AM_DVD_CONNECT_DIRECTONLY) ;
if (FAILED(hr)) // what?!?
{
ASSERT(FALSE) ; // so that we notice
DbgLog((LOG_TRACE, 1, TEXT("No video out pin connected to pin %s -- no video on screen"),
(LPCTSTR)CDisp(pPinIn))) ;
}
else
{
bConnected = TRUE ;
}
pPinIn->Release() ; // done with VR's in pin
}
else // what?!?
{
DbgLog((LOG_TRACE, 1, TEXT("No input pin of VideoRenderer?!?"))) ;
// Remove it from graph; else a useless window will pop up
m_pGB->RemoveFilter(m_pVR) ;
m_pVR->Release() ;
m_pVR = NULL ;
}
}
else // what?!?
{
ASSERT(FALSE) ; // so that we notice
DbgLog((LOG_TRACE, 1,
TEXT("WARNING: Can't start Video Renderer (Error 0x%lx) -- no video on screen"),
hr)) ;
// bConnected = FALSE ;
}
pPinOutOvM->Release() ; // done with OvMixer's out pin
if (! bConnected ) // if connection to OvMixer's in pin failed => no video on screen
{
DbgLog((LOG_TRACE, 1, TEXT("WARNING: Couldn't render Video stream using OvMixer"))) ;
pStatus->iNumStreamsFailed++ ;
pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
return S_FALSE ;
}
return S_OK ;
}
HRESULT CDvdGraphBuilder::RenderVideoUsingVMR(IPin **apPinOut,
AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderVideoUsingVMR(0x%lx, 0x%lx)"),
apPinOut, pStatus)) ;
HRESULT hr ;
IPin *pPinIn ;
BOOL bConnected = FALSE ; // until connects
//
// If OvMixer has somehow been instantiated, we need to remove and release it now.
//
if (m_pOvM)
{
DbgLog((LOG_TRACE, 3, TEXT("OvMixer was somehow created. Can't use VMR now."))) ;
return E_FAIL ;
// DbgLog((LOG_TRACE, 3, TEXT("OvMixer was somehow created and not in use. Removing it..."))) ;
// Remove it from graph and release it
// m_pGB->RemoveFilter(m_pOvM) ;
// m_pOvM->Release() ;
// m_pOvM = NULL ;
}
//
// Now instantiate VMR and try to render using it
//
hr = EnsureVMRExists() ;
if (S_OK != hr)
{
DbgLog((LOG_TRACE, 3, TEXT("Video Mixing Renderer couldn't be started or configured"))) ;
// pStatus->iNumStreamsFailed++ ;
// pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
return E_FAIL ; // caught by RenderDecodedVideo()
}
// Connect given output pin to VMR's first input pin
hr = FindMatchingPin(m_pVMR, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ;
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 1, TEXT("WARNING: No open input pin found on VMR (Error 0x%lx)"), hr)) ;
ASSERT(FALSE) ; // so that we know of this weird case
// Remove it from graph; it's useless now
m_pGB->RemoveFilter(m_pVMR) ;
m_pVMR->Release() ;
m_pVMR = NULL ;
// pStatus->iNumStreamsFailed++ ;
// pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
return E_UNEXPECTED ; // caught by RenderDecodedVideo(), but unexpected
}
// Try to connect the first out pin of the video decoder to VMR's 1st in pin
int i = 0 ;
while (!bConnected && i < MAX_DEC_OUT_PINS && apPinOut[i])
{
hr = ConnectPins(apPinOut[i], pPinIn, AM_DVD_CONNECT_DIRECTFIRST) ;
if (FAILED(hr))
{
pStatus->hrVPEStatus = hr ;
i++ ;
ASSERT(i <= MAX_DEC_OUT_PINS) ;
}
else
{
bConnected = TRUE ;
pStatus->hrVPEStatus = S_OK ; // make sure we don't return any error code
}
}
pPinIn->Release() ; // done with the pin
if (! bConnected ) // if connection to VMR's in pin failed => no video on screen
{
DbgLog((LOG_TRACE, 1, TEXT("WARNING: Couldn't render Video stream using VMR"))) ;
// Remove it from graph; it's useless now
m_pGB->RemoveFilter(m_pVMR) ;
m_pVMR->Release() ;
m_pVMR = NULL ;
// pStatus->iNumStreamsFailed++ ;
// pStatus->dwFailedStreamsFlag |= AM_DVD_STREAM_VIDEO ;
return E_UNEXPECTED ; // S_FALSE ;
}
return S_OK ;
}
HRESULT CDvdGraphBuilder::RenderVideoUsingVPM(IPin **apPinOut,
AM_DVD_RENDERSTATUS *pStatus,
IPin **apPinOutVPM)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderVideoUsingVPM(0x%lx, 0x%lx, 0x%lx)"),
apPinOut, pStatus, apPinOutVPM)) ;
HRESULT hr ;
IPin *pPinIn ;
IPin *pPinOut ;
BOOL bConnected = FALSE ; // until connects
// Filter, hopefully decoder, has been connected to the Nav.
// Now try to connect it to VPM (and later to VMR).
ASSERT(NULL == m_pVPM) ;
// *apPinOutVPM = NULL ; // to start with
hr = CreateFilterInGraph(CLSID_VideoPortManager, L"Video Port Manager", &m_pVPM) ;
if (FAILED(hr))
{
DbgLog((LOG_TRACE, 3, TEXT("VPM couldn't be started!!!"))) ;
return E_FAIL ; // caught by RenderDecodedVideo()
}
// Connect given output pin to VPM's first input pin
hr = FindMatchingPin(m_pVPM, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ;
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 1, TEXT("WARNING: No open input pin found on VPM (Error 0x%lx)"), hr)) ;
ASSERT(FALSE) ; // so that we know of this weird case
// Remove it from graph; it's useless now
m_pGB->RemoveFilter(m_pVPM) ;
m_pVPM->Release() ;
m_pVPM= NULL ;
return E_UNEXPECTED ; // caught by RenderDecodedVideo()
}
// Try to connect the first out pin of the HW video decoder to VPM's in pin
int i = 0 ;
while (!bConnected && i < MAX_DEC_OUT_PINS && apPinOut[i])
{
hr = ConnectPins(apPinOut[i], pPinIn, AM_DVD_CONNECT_DIRECTFIRST) ;
if (FAILED(hr))
{
pStatus->hrVPEStatus = hr ;
i++ ;
ASSERT(i <= MAX_DEC_OUT_PINS) ;
}
else
{
bConnected = TRUE ;
pStatus->hrVPEStatus = S_OK ; // make sure we don't return any error code
}
}
pPinIn->Release() ; // done with the pin
if (! bConnected ) // if connection to VPM's in pin failed => no video on screen
{
DbgLog((LOG_TRACE, 1, TEXT("WARNING: Couldn't render (HW) Video stream using VPM"))) ;
// Remove it from graph; it's useless now
m_pGB->RemoveFilter(m_pVPM) ;
m_pVPM->Release() ;
m_pVPM = NULL ;
return E_FAIL ;
}
// Connected!! Now find the first out pin of the VPM (to connect to VMR).
hr = FindMatchingPin(m_pVPM, 0, PINDIR_OUTPUT, TRUE, 0, &pPinOut) ;
ASSERT(SUCCEEDED(hr)) ;
apPinOutVPM[0] = pPinOut ; // only one pin returned; release in the caller
return hr ;
}
HRESULT CDvdGraphBuilder::RenderDecodedVideo(IPin **apPinOut,
AM_DVD_RENDERSTATUS *pStatus,
DWORD dwDecFlag)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderDecodedVideo(0x%lx, 0x%lx, 0x%lx)"),
apPinOut, pStatus, dwDecFlag)) ;
HRESULT hr = S_OK ;
// SWDecodeDVDStream() method tried to detect if the video/SP decoder is
// VMR-compatible. If not, it has set a flag (m_bTryVMR to FALSE), so here
// we know which renderer to use.
//
// For hardware decoders that work with VPE, we don't check the VMR-compatibility.
// We just try to connect it to VPM and VMR. If that doesn't work, use OvMixer. We
// avoid trying to connect the non-VPE decoders to VPM, because we know that DXR2,
// which uses analog overlay, gets completely messed up if it's even attempted to
// connect to VPM (which fails anyway).
//
// We first try to use VMR, if we are supposed to, i.e.,
// a) DDraw (non-)exclusive mode is NOT being used
// b) the decoder(s) is VMR compatible
// c) no one has asked us not to (in some other way)
// If that succeeds, Great!!! Otherwise we fall back on using OvMixer, so
// that we can at least play the DVD.
// In case we try to use OvMixer, and that for some reason fails to connect,
// the error flags and code are set in RenderVideoUsingOvMixer() method.
//
if (GetVMRUse()) // VMR can be used (so far)
{
//
// We should try to use VMR first....
//
// If HW decoder filter is being used, we check if the output type is VPVideo,
// and then only we'll try to use VPM for VMR first. If that works, we'll
// return the out pin of VPM. If it fails, we'll set a flag so that
// RenderDecodedVideo() method knows and uses OvMixer as a fallback option.
// If the output mediatype is non-VPE (e.g., analog overlay), we do NOT even
// try to connect to VPM, and fall back to OvMixer.
//
if (AM_DVD_HWDEC_ONLY == dwDecFlag) // video decoded in HW
{
DbgLog((LOG_TRACE, 5, TEXT("HW decoder used for Video. Is it VMR-compatible?"))) ;
if (IsOutputTypeVPVideo(apPinOut[0])) // output type is VPE => use VPM
{ // Checking the first out pin should be fine
// VPVideo stream: Try to use VPM and VMR for rendering
DbgLog((LOG_TRACE, 5, TEXT("HW decoder with VPE -- connect to VPM+VMR"))) ;
IPin *apPinOutVPM[2] ; // There is only one out pin of VPM (one for NULL)
ResetPinInterface(apPinOutVPM, NUMELMS(apPinOutVPM)) ;
hr = RenderVideoUsingVPM(apPinOut, pStatus, apPinOutVPM) ; // returns VPM's out pin
// If the success status is still maintained, use the VMR.
if (SUCCEEDED(hr))
{
DbgLog((LOG_TRACE, 5, TEXT("HW decoder connected to VPM. Now connect to VMR."))) ;
hr = RenderVideoUsingVMR(apPinOutVPM, pStatus) ; // render VPM's out pin via VMR
ReleasePinInterface(apPinOutVPM) ; // done with VPM out pin interface
if (FAILED(hr))
{
DbgLog((LOG_TRACE, 5, TEXT("VPM - VMR connection failed. Removing VPM..."))) ;
if (m_pVPM)
{
EXECUTE_ASSERT(SUCCEEDED(m_pGB->RemoveFilter(m_pVPM))) ;
m_pVPM->Release() ;
m_pVPM = NULL ;
}
}
}
else
{
ReleasePinInterface(apPinOutVPM) ; // shouldn't be needed, but...
}
} // end of if (VPVideo)
else // output type is not VPE => use OvMixer (Not VPM+VMR)
{
DbgLog((LOG_TRACE, 5, TEXT("Non-VPE HW decoder -- didn't try VPM+VMR"))) ;
hr = E_FAIL ; // set failure code so that it's tried with OvMixer below
}
} // end of if (HW decoder used)
else // we are using SW video decoder -- render directly using VMR
{
DbgLog((LOG_TRACE, 5, TEXT("HW decoder not used. Directly connect to VMR..."))) ;
hr = RenderVideoUsingVMR(apPinOut, pStatus) ;
}
// In case anything above failed, ditch VMR, and go for OvMixer.
if (FAILED(hr))
{
DbgLog((LOG_TRACE, 4, TEXT("Render using VMR failed. Falling back on OvMixer..."))) ;
//
// NOTE: If we can't use VMR for video, no point trying it for SP stream
//
SetVMRUse(FALSE) ;
hr = RenderVideoUsingOvMixer(apPinOut, pStatus) ;
}
}
else // we are not supposed to use VMR; that means use OvMixer
{
hr = RenderVideoUsingOvMixer(apPinOut, pStatus) ;
}
return hr ;
}
HRESULT CDvdGraphBuilder::RenderDecodedAudio(IPin **apPinOut, AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderDecodedAudio(0x%lx, 0x%lx)"),
apPinOut, pStatus)) ;
HRESULT hr ;
HRESULT hrFinal = S_OK ;
BOOL bConnected = FALSE ; // until connected
IPin *pPinIn = NULL ;
ASSERT(NULL == m_pAR) ; // so that we know
// Create the Audio Renderer filter and connect decoder's audio out pin to that
hr = CreateFilterInGraph(CLSID_DSoundRender, L"DSound Renderer", &m_pAR) ;
if (SUCCEEDED(hr))
{
// Get an input pin to Audio Renderer
hr = FindMatchingPin(m_pAR, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ;
ASSERT(SUCCEEDED(hr) && pPinIn) ;
}
else
{
ASSERT(! TEXT("Coundn't start Audio Renderer") ) ; // so that we notice
DbgLog((LOG_TRACE, 1,
TEXT("WARNING: Can't start Audio Renderer (Error 0x%lx) -- no audio from speakers"),
hr)) ;
hrFinal = S_FALSE ; // no audio from speakers -- result downgraded
}
//
// We'll try to render all the decoded audio out pins
//
for (int i = 0 ; i < MAX_DEC_OUT_PINS && apPinOut[i]; i++)
{
if (pPinIn) // if we have an open input pin of Audio Renderer
{
hr = m_pGB->Connect(apPinOut[i], pPinIn) ;
if (SUCCEEDED(hr)) // decoded audio connected to audio renderer
{
DbgLog((LOG_TRACE, 5, TEXT("Pin %s connected to pin %s"),
(LPCTSTR)CDisp(apPinOut[i]), (LPCTSTR)CDisp(pPinIn))) ;
EnumFiltersBetweenPins(AM_DVD_STREAM_AUDIO, apPinOut[i], pPinIn, pStatus) ;
bConnected = TRUE ;
pPinIn->Release() ; // done with this pin interface
pPinIn = NULL ;
// Let's try the next out pin, if any...
continue ;
}
ASSERT(!TEXT("Couldn't connect audio pin")) ; // so that we notice
DbgLog((LOG_TRACE, 1, TEXT("Pin %s (#%ld) did NOT connect to pin %s"),
(LPCTSTR)CDisp(apPinOut[i]), i, (LPCTSTR)CDisp(pPinIn))) ;
}
//
// We could come here, because either
// 1. DSound Renderer didn't start (no audio device)
// 2. we couldn't get an in pin to DSound Renderer (impossible, but...)
//
// Couldn't connect the outout pin to a known renderer. Let's try to
// just render, and see if any filter (S/PDIF?) connects to it.
//
hr = m_pGB->Render(apPinOut[i]) ;
if (FAILED(hr))
{
ASSERT(!TEXT("Audio out pin didn't render at all")) ; // so that we notice
DbgLog((LOG_TRACE, 1, TEXT("Pin %s (#%ld) did NOT render at all"),
(LPCTSTR)CDisp(apPinOut[i]), i)) ;
}
// Now onto the next decoded audio out pin, if any...
} // end of while (i ...) loop
if (! bConnected ) // connection to Audio Renderer failed => no audio on speakers
{
DbgLog((LOG_TRACE, 1,
TEXT("No decoded audio pin connect to AudioRenderer -- no audio from speakers"))) ;
if (m_pAR) // if we had an Audio Renderer
{
if (pPinIn) // if we had an in pin that we couldn't connect to,
pPinIn->Release() ; // let it go now.
// Remove Audio Renderer from graph
m_pGB->RemoveFilter(m_pAR) ;
m_pAR->Release() ;
m_pAR = NULL ;
}
hrFinal = S_FALSE ;
}
return hrFinal ;
}
HRESULT CDvdGraphBuilder::RenderSubpicUsingOvMixer(IPin **apPinOut,
AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderSubpicUsingOvMixer(0x%lx, 0x%lx)"),
apPinOut, pStatus)) ;
HRESULT hr ;
BOOL bConnected = FALSE ; // until connected
IPin *pPinIn ;
int i = 0 ;
ASSERT(m_pOvM) ; // it must be there, if video stream was rendered
// Now connect the given out pin to the next available in pin of OvMixer
hr = FindMatchingPin(m_pOvM, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ;
if (SUCCEEDED(hr) && pPinIn)
{
while (!bConnected && i < MAX_DEC_OUT_PINS && apPinOut[i])
{
hr = ConnectPins(apPinOut[i], pPinIn, AM_DVD_CONNECT_DIRECTONLY) ;
if (FAILED(hr)) // what?!?
{
// ASSERT(FALSE) ; // so that we notice
DbgLog((LOG_TRACE, 1, TEXT("Pin %s (#%ld) did NOT connect to pin %s -- no SP"),
(LPCTSTR)CDisp(apPinOut[i]), i, (LPCTSTR)CDisp(pPinIn))) ;
i++ ;
ASSERT(i <= MAX_DEC_OUT_PINS) ;
}
else
{
DbgLog((LOG_TRACE, 5, TEXT("Pin %s is directly connected to pin %s"),
(LPCTSTR)CDisp(apPinOut[i]), (LPCTSTR)CDisp(pPinIn))) ;
bConnected = TRUE ;
}
}
pPinIn->Release() ; // done with OvMixer's in pin
}
else // what?!?
{
DbgLog((LOG_TRACE, 1, TEXT("WARNING: No more input pin of OverlayMixer?!?"))) ;
}
return (bConnected ? S_OK : hr) ; // this should be the same as "return hr ;"
}
HRESULT CDvdGraphBuilder::RenderSubpicUsingVMR(IPin **apPinOut,
AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderSubpicUsingVMR(0x%lx, 0x%lx)"),
apPinOut, pStatus)) ;
HRESULT hr ;
BOOL bConnected = FALSE ; // until connected
IPin *pPinIn ;
int i = 0 ;
ASSERT(m_pVMR) ; // it must be there, if video stream was rendered
// Now connect the given out pin to the next available in pin of VMR
hr = FindMatchingPin(m_pVMR, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ;
if (SUCCEEDED(hr) && pPinIn)
{
while (!bConnected && i < MAX_DEC_OUT_PINS && apPinOut[i])
{
hr = ConnectPins(apPinOut[i], pPinIn, AM_DVD_CONNECT_DIRECTONLY) ;
if (FAILED(hr)) // what?!?
{
// ASSERT(FALSE) ; // so that we notice
DbgLog((LOG_TRACE, 1, TEXT("Pin %s (#%ld) did NOT connect to pin %s -- no SP"),
(LPCTSTR)CDisp(apPinOut[i]), i, (LPCTSTR)CDisp(pPinIn))) ;
i++ ;
ASSERT(i <= MAX_DEC_OUT_PINS) ;
}
else
{
DbgLog((LOG_TRACE, 5, TEXT("Pin %s is directly connected to pin %s"),
(LPCTSTR)CDisp(apPinOut[i]), (LPCTSTR)CDisp(pPinIn))) ;
bConnected = TRUE ;
}
}
pPinIn->Release() ; // done with VMR's in pin
}
else // what?!?
{
DbgLog((LOG_TRACE, 1, TEXT("WARNING: No more input pin of VMR?!?"))) ;
}
return (bConnected ? S_OK : hr) ; // this should be the same as "return hr ;"
}
HRESULT CDvdGraphBuilder::RenderDecodedSubpic(IPin **apPinOut, AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderDecodedSubpic(0x%lx, 0x%lx)"),
apPinOut, pStatus)) ;
HRESULT hr ;
//
// Render the decoded subpicture stream ONLY IF the user wants that
//
if (!m_bUseVPE || IsDDrawExclMode())
{
DbgLog((LOG_TRACE, 1, TEXT("SP Stream: RenderDvdVideoVolume() skipped for %s and %s"),
m_bUseVPE ? "VPE" : "no VPE", IsDDrawExclMode() ? "DDraw excl mode" : "normal mode")) ;
return S_OK ;
}
// We have already attempted to render the video straem. If that has failed,
// there is no point trying to render the subpicture stream -- just indicate
// that this stream didn't render and return.
if (pStatus->dwFailedStreamsFlag & AM_DVD_STREAM_VIDEO)
{
DbgLog((LOG_TRACE, 1, TEXT("WARNING: Video stream didn't render. Skipping SP rendering."))) ;
return S_FALSE ;
}
if (GetVMRUse()) // if VMR is to be used
{
DbgLog((LOG_TRACE, 5, TEXT("Rendering SP stream using VMR"))) ;
hr = RenderSubpicUsingVMR(apPinOut, pStatus) ;
}
else // OvMixer is being used
{
DbgLog((LOG_TRACE, 5, TEXT("Rendering SP stream using OvMixer"))) ;
hr = RenderSubpicUsingOvMixer(apPinOut, pStatus) ;
}
//
// We don't set the following flag and values anymore as part of the hack
// to ignore failure to connect *some* decoded-SP-ish out pin in the case
// of HW decoders. The caller of this method knows if the decoder being
// used is HW or SW and based on that it will ignore any failure or not.
//
if (FAILED(hr)) // if connection to OvMixer's in pin failed => no SP (weird!!)
{
DbgLog((LOG_TRACE, 1, TEXT("WARNING: Subpic pin could NOT connect to renderer"))) ;
return hr ; // S_FALSE ;
}
return S_OK ; // complete success!!!
}
HRESULT CDvdGraphBuilder::RenderLine21Stream(IPin *pPinOut, AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderLine21Stream(%s, 0x%lx)"),
(LPCTSTR)CDisp(pPinOut), pStatus)) ;
HRESULT hr ;
BOOL bConnected = FALSE ; // until connected
IPin *pPinIn ;
ASSERT(NULL == m_pL21Dec) ; // so that we know
//
// Create the Line21 Decoder filter and connect given out pin to that
//
if (GetVMRUse()) // for VMR use Line21 Decoder2
{
hr = CreateFilterInGraph(CLSID_Line21Decoder2, L"Line21 Decoder2", &m_pL21Dec) ;
}
else // for OvMixer, keep using the old one
{
hr = CreateFilterInGraph(CLSID_Line21Decoder, L"Line21 Decoder", &m_pL21Dec) ;
}
if (SUCCEEDED(hr))
{
hr = FindMatchingPin(m_pL21Dec, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ;
if (SUCCEEDED(hr) && pPinIn)
{
hr = ConnectPins(pPinOut, pPinIn, AM_DVD_CONNECT_DIRECTONLY) ;
if (FAILED(hr)) // what?!?
{
ASSERT(FALSE) ;
DbgLog((LOG_TRACE, 1, TEXT("Pin %s did NOT connect to pin %s -- no CC"),
(LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
pPinIn->Release() ; // release pin before removing filter
m_pGB->RemoveFilter(m_pL21Dec) ;
m_pL21Dec->Release() ;
m_pL21Dec = NULL ;
}
else
{
bConnected = TRUE ;
pPinIn->Release() ; // because we do so for the failure case
}
}
else // what?!?
{
DbgLog((LOG_TRACE, 1, TEXT("No input pin of Line21 Decoder(2)?!?"))) ;
// Remove it from graph
m_pGB->RemoveFilter(m_pL21Dec) ;
m_pL21Dec->Release() ;
m_pL21Dec = NULL ;
}
}
else // what?!?
{
// ASSERT(FALSE) ; // so that we notice -- not until lin21dec2 is done
DbgLog((LOG_TRACE, 1,
TEXT("WARNING: Can't start Line21 Decoder(2) (Error 0x%lx) -- no CC"),
hr)) ;
}
if (! bConnected ) // if connection to OvMixer's in pin failed => no video on screen
{
DbgLog((LOG_TRACE, 1, TEXT("WARNING: Pin %s could NOT connect to Line21 Decoder(2)"),
(LPCTSTR)CDisp(pPinOut))) ;
return hr ;
}
// Now connect line21 decoder(2)'s output to OvMixer/VMR's in pin
bConnected = FALSE ; // until connected again
IPin *pPinOutL21 ;
hr = FindMatchingPin(m_pL21Dec, 0, PINDIR_OUTPUT, TRUE, 0, &pPinOutL21) ;
ASSERT(SUCCEEDED(hr)) ;
if (GetVMRUse()) // find VMR's in pin
{
hr = FindMatchingPin(m_pVMR, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ; // Caution: reusing pPinIn
}
else // find OvMixer's in pin
{
hr = FindMatchingPin(m_pOvM, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ; // Caution: reusing pPinIn
}
ASSERT(SUCCEEDED(hr)) ;
if (pPinOutL21 && pPinIn)
{
hr = ConnectPins(pPinOutL21, pPinIn, AM_DVD_CONNECT_DIRECTONLY) ;
if (FAILED(hr)) // what?!?
{
ASSERT(FALSE) ; // so that we notice
DbgLog((LOG_TRACE, 1, TEXT("Pin %s did NOT connect to pin %s -- no CC"),
(LPCTSTR)CDisp(pPinOutL21), (LPCTSTR)CDisp(pPinIn))) ;
pPinOutL21->Release() ; // release pin before removing filter
m_pGB->RemoveFilter(m_pL21Dec) ;
m_pL21Dec->Release() ;
m_pL21Dec = NULL ;
}
else
{
bConnected = TRUE ;
pPinOutL21->Release() ; // because we do so in the failure case
}
pPinIn->Release() ; // done with OvMixer's in pin
}
else
{
DbgLog((LOG_TRACE, 1, TEXT("WARNING: Couldn't get necessary in/out pin"))) ;
if (pPinIn)
pPinIn->Release() ;
if (pPinOutL21)
pPinOutL21->Release() ;
// Remove it from graph
m_pGB->RemoveFilter(m_pL21Dec) ;
m_pL21Dec->Release() ;
m_pL21Dec = NULL ;
}
if (! bConnected ) // if connection to OvMixer's in pin failed => no CC
{
DbgLog((LOG_TRACE, 1, TEXT("WARNING: Line21Dec output could NOT connect to OvMixer/VMR"))) ;
return hr ;
}
return S_OK ; // complete success!!!
}
BOOL CDvdGraphBuilder::IsOutputDecoded(IPin *pPinOut)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::IsOutputDecoded(%s)"),
(LPCTSTR)CDisp(pPinOut))) ;
HRESULT hr ;
IEnumMediaTypes *pEnumMT ;
AM_MEDIA_TYPE *pmt ;
ULONG ul ;
BOOL bMTDecoded = FALSE ; // unless found otherwise
hr = pPinOut->EnumMediaTypes(&pEnumMT) ;
ASSERT(SUCCEEDED(hr) && pEnumMT) ;
while ( !bMTDecoded &&
S_OK == pEnumMT->Next(1, &pmt, &ul) && 1 == ul)
{
#if 1 // we'll use the following procedure
bMTDecoded = (MEDIATYPE_Video == pmt->majortype && // major type Video,
MEDIASUBTYPE_MPEG2_VIDEO != pmt->subtype && // subtype is NOT MPEG2Video
MEDIASUBTYPE_DVD_SUBPICTURE != pmt->subtype) || // subtype is NOT DVDSubPicture OR
(MEDIATYPE_Audio == pmt->majortype && // major type Audio
MEDIASUBTYPE_MPEG2_AUDIO != pmt->subtype && // subtype is NOT MPEG2Audio
MEDIASUBTYPE_DOLBY_AC3 != pmt->subtype && // subtype is NOT Dolby AC3
MEDIASUBTYPE_DVD_LPCM_AUDIO != pmt->subtype) || // subtype is NOT DVD-LPCMAudio
(MEDIATYPE_AUXLine21Data == pmt->majortype) ; // majortype is Line21
#else // not this procedure
bMTDecoded = (MEDIATYPE_DVD_ENCRYPTED_PACK != pmt->majortype && // majortype is NOT DVD_ENCRYPTED_PACK
MEDIATYPE_MPEG2_PES != pmt->majortype && // majortype is NOT MPEG2_PES
MEDIASUBTYPE_MPEG2_VIDEO != pmt->subtype && // subtype is NOT MPEG2Video
MEDIASUBTYPE_MPEG2_AUDIO != pmt->subtype && // subtype is NOT MPEG2Audio
MEDIASUBTYPE_DOLBY_AC3 != pmt->subtype && // subtype is NOT DolbyAC3
MEDIASUBTYPE_DVD_LPCM_AUDIO != pmt->subtype && // subtype is NOT DVD_LPCMAudio
MEDIASUBTYPE_DVD_SUBPICTURE != pmt->subtype) ; // subtype is NOT DVD_SUBPICTURE
#endif // #if 1
DeleteMediaType(pmt) ; // otherwise
}
pEnumMT->Release() ;
return bMTDecoded ;
}
BOOL CDvdGraphBuilder::IsOutputTypeVPVideo(IPin *pPinOut)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::IsOutputTypeVPVideo(%s)"),
(LPCTSTR)CDisp(pPinOut))) ;
HRESULT hr ;
IEnumMediaTypes *pEnumMT ;
AM_MEDIA_TYPE *pmt ;
ULONG ul ;
BOOL bVPVideo = FALSE ; // unless found otherwise
hr = pPinOut->EnumMediaTypes(&pEnumMT) ;
ASSERT(SUCCEEDED(hr) && pEnumMT) ;
while ( !bVPVideo &&
S_OK == pEnumMT->Next(1, &pmt, &ul) && 1 == ul)
{
bVPVideo = MEDIATYPE_Video == pmt->majortype && // major type Video,
MEDIASUBTYPE_VPVideo == pmt->subtype ; // subtype is VPVideo
DeleteMediaType(pmt) ; // otherwise
}
pEnumMT->Release() ;
return bVPVideo ;
}
//
// Create a filter and add it to the filter graph
//
HRESULT CDvdGraphBuilder::CreateFilterInGraph(CLSID Clsid,
LPCWSTR lpszwFilterName,
IBaseFilter **ppFilter)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::CreateFilterInGraph(%s, %S, 0x%lx)"),
(LPCTSTR) CDisp(Clsid), lpszwFilterName, 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
hr = m_pGB->AddFilter(*ppFilter, lpszwFilterName) ;
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 ;
}
//
// Instantiate all the HW decoders registered under DVD Hardware Decoder
// group under the Active Filters category.
//
// Qn: Do we need to also pick up the HW filters under any other category,
// specially for filters like the external AC3 decoder etc.?
//
HRESULT CDvdGraphBuilder::CreateDVDHWDecoders(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::CreateDVDHWDecoders()"))) ;
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 ;
while (S_OK == pEnumMon->Next(1, &pMon, &ul) && 1 == ul)
{
#ifdef DEBUG
WCHAR *wszName ;
pMon->GetDisplayName(0, 0, &wszName) ;
DbgLog((LOG_TRACE, 5, TEXT("Moniker enum: %S"), wszName)) ;
CoTaskMemFree(wszName) ;
#endif // DEBUG
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)
{
#ifdef DEBUG
{
VARIANT var ;
var.vt = VT_EMPTY ;
hr = pPropBag->Read(L"DevicePath", &var, 0) ;
ASSERT(SUCCEEDED(hr)) ;
DbgLog((LOG_TRACE, 5, TEXT("DevicePath: %S"), var.bstrVal)) ;
VariantClear(&var) ;
}
{
VARIANT var ;
var.vt = VT_EMPTY ;
hr = pPropBag->Read(L"CLSID", &var, 0) ;
ASSERT(SUCCEEDED(hr)) ;
DbgLog((LOG_TRACE, 5, TEXT("CLSID: %S"), var.bstrVal)) ;
VariantClear(&var) ;
}
#endif // DEBUG
{
VARIANT var ;
var.vt = VT_EMPTY ;
hr = pPropBag->Read(L"FriendlyName", &var, 0) ;
if (SUCCEEDED(hr))
{
DbgLog((LOG_TRACE, 5, TEXT("FriendlyName: %S"), var.bstrVal)) ;
//
// 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.
//
m_ListHWDecs.AddFilter(pFilter, var.bstrVal, NULL) ;
VariantClear(&var) ;
}
else
{
DbgLog((LOG_ERROR, 1, TEXT("WARNING: Failed to get FriendlyName (Error 0x%lx)"), hr)) ;
ASSERT(SUCCEEDED(hr)) ; // so that we know
}
}
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"), m_ListHWDecs.GetCount())) ;
return NOERROR ;
}
HRESULT CDvdGraphBuilder::FindMatchingPin(IBaseFilter *pFilter, DWORD dwStream, PIN_DIRECTION pdWanted,
BOOL bOpen, int iIndex, IPin **ppPin)
{
DbgLog((LOG_TRACE, 4,
TEXT("CDvdGraphBuilder::FindMatchingPin(0x%lx, 0x%lx, %s, %s, %d, 0x%lx)"),
pFilter, dwStream, pdWanted == PINDIR_INPUT ? "In" : "Out",
bOpen ? "T" : "F", iIndex, ppPin)) ;
HRESULT hr = E_FAIL ;
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 ;
}
EXECUTE_ASSERT(SUCCEEDED(pFilter->EnumPins(&pEnumPins))) ;
ASSERT(pEnumPins) ;
while (S_OK == pEnumPins->Next(1, &pPin, &ul) && 1 == ul)
{
EXECUTE_ASSERT(SUCCEEDED(pPin->QueryDirection(&pdFound))) ;
if (pdWanted != pdFound)
{
pPin->Release() ; // don't need this pin
continue ;
}
HRESULT hr1 = pPin->ConnectedTo(&pPin2) ;
ASSERT((SUCCEEDED(hr1) && pPin2) || (FAILED(hr1) && !pPin2)) ;
if (bOpen) // we looking for an open pin
{
if (SUCCEEDED(hr1) && pPin2) // pin already connected -- skip it
{
pPin2->Release() ; // not interested in this pin actually
pPin->Release() ; // this pin is already connected -- skip it
continue ; // try next one
}
// Otherwise we have got an open pin -- onto the mediatypes...
// Check mediatype only if a streamtype was specified
if (0 != dwStream && dwStream != GetPinStreamType(pPin) ) // not a mediatype match
{
DbgLog((LOG_TRACE, 5, TEXT("Pin %s is not of stream type 0x%lx"),
(LPCTSTR) CDisp(pPin), dwStream)) ;
pPin->Release() ; // this pin is already connected -- skip it
continue ; // try next one
}
}
else // we looking for a connected pin
{
if (FAILED(hr1) || NULL == pPin2) // pin NOT connected -- skip it
{
pPin->Release() ; // this pin is NOT connected -- skip it
continue ; // try next one
}
// Otherwise we have got a connected pin
pPin2->Release() ; // else we leak!!!
// Check mediatype only if a streamtype was specified
if (0 != dwStream)
{
AM_MEDIA_TYPE mt ;
pPin->ConnectionMediaType(&mt) ;
if (dwStream != GetStreamFromMediaType(&mt))
{
DbgLog((LOG_TRACE, 5, TEXT("Pin %s is not of stream type 0x%lx"),
(LPCTSTR) CDisp(pPin), dwStream)) ;
FreeMediaType(mt) ; // else we leak
pPin->Release() ; // this pin is already connected -- skip it
continue ; // try next one
}
FreeMediaType(mt) ; // anyway have to free this
}
}
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
}
DWORD CDvdGraphBuilder::GetStreamFromMediaType(AM_MEDIA_TYPE *pmt)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::GetStreamFromMediaType(0x%lx)"), pmt)) ;
DWORD dwStream = 0 ;
// 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"),
(LPCTSTR) CDisp(pmt->subtype))) ;
}
}
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 if (pmt->subtype == MEDIASUBTYPE_RGB8 ||
pmt->subtype == MEDIASUBTYPE_RGB565 ||
pmt->subtype == MEDIASUBTYPE_RGB555 ||
pmt->subtype == MEDIASUBTYPE_RGB24 ||
pmt->subtype == MEDIASUBTYPE_RGB32)
{
DbgLog((LOG_TRACE, 5, TEXT("Subtype is RGB8/16/24/32"))) ;
dwStream = AM_DVD_STREAM_VIDEO ;
}
else
{
DbgLog((LOG_TRACE, 5, TEXT("Unknown subtype %s for Video -- assuming decoded video"),
(LPCTSTR) CDisp(pmt->subtype))) ;
dwStream = AM_DVD_STREAM_VIDEO ;
}
}
else if (pmt->majortype == MEDIATYPE_Audio) // elementary stream
{
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is Audio elementary"))) ;
if (pmt->subtype == MEDIASUBTYPE_DOLBY_AC3)
{
DbgLog((LOG_TRACE, 5, TEXT("Subtype is AC3"))) ;
dwStream = AM_DVD_STREAM_AUDIO ;
}
if (pmt->subtype == MEDIASUBTYPE_MPEG2_AUDIO)
{
DbgLog((LOG_TRACE, 5, TEXT("Subtype is MPEG2_AUDIO"))) ;
dwStream = AM_DVD_STREAM_AUDIO ;
}
if (pmt->subtype == MEDIASUBTYPE_DVD_LPCM_AUDIO)
{
DbgLog((LOG_TRACE, 5, TEXT("Subtype is DVD_LPCM Audio"))) ;
dwStream = AM_DVD_STREAM_AUDIO ;
}
else
{
DbgLog((LOG_TRACE, 5, TEXT("Unknown subtype %s for Audio -- assuming decoded audio"),
(LPCTSTR) CDisp(pmt->subtype))) ;
dwStream = AM_DVD_STREAM_AUDIO ;
}
}
else if (pmt->majortype == MEDIATYPE_AUXLine21Data) // line21 stream
{
ASSERT(pmt->subtype == MEDIASUBTYPE_Line21_GOPPacket) ; // just checking
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is Line21 GOPPacket"))) ;
dwStream = AM_DVD_STREAM_LINE21 ;
}
else if (pmt->majortype == MEDIATYPE_Stream) // some stream format
{
if (pmt->subtype == MEDIASUBTYPE_Asf) // ASF stream
{
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is ASF stream"))) ;
dwStream = AM_DVD_STREAM_ASF ;
}
else // some other stream format
{
DbgLog((LOG_TRACE, 5, TEXT("Mediatype is some OTHER stream format"))) ;
dwStream = AM_DVD_STREAM_ADDITIONAL ;
}
}
//
// 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..."),
(LPCTSTR) CDisp(pmt->majortype), (LPCTSTR) CDisp(pmt->subtype))) ;
if (pmt->subtype == MEDIASUBTYPE_DOLBY_AC3 ||
pmt->subtype == MEDIASUBTYPE_MPEG2_AUDIO ||
pmt->subtype == MEDIASUBTYPE_DVD_LPCM_AUDIO)
{
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
{
DbgLog((LOG_TRACE, 2, TEXT("WARNING: Unknown mediatype. Couldn't detect at all."))) ;
}
}
return dwStream ;
}
DWORD CDvdGraphBuilder::GetPinStreamType(IPin *pPin)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::GetPinStreamType(%s)"),
(LPCTSTR) CDisp(pPin))) ;
DWORD dwStream = 0 ;
AM_MEDIA_TYPE *pmt ;
IEnumMediaTypes *pEnumMT ;
ULONG ul ;
HRESULT hr = pPin->EnumMediaTypes(&pEnumMT) ;
ASSERT(SUCCEEDED(hr) && pEnumMT) ;
while (0 == dwStream &&
S_OK == pEnumMT->Next(1, &pmt, &ul) && 1 == ul) // more mediatypes
{
dwStream = GetStreamFromMediaType(pmt) ;
DeleteMediaType(pmt) ;
} // end of while()
pEnumMT->Release() ;
return dwStream ; // whatever we found
}
HRESULT CDvdGraphBuilder::GetFilterCLSID(IBaseFilter *pFilter, DWORD dwStream,
LPCWSTR lpszwName, GUID *pClsid)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::GetFilterCLSID(0x%lx, 0x%lx, %S, 0x%lx)"),
pFilter, dwStream, lpszwName, pClsid)) ;
HRESULT hr ;
IEnumRegFilters *pEnumFilters ;
REGFILTER *pRegFilter ;
IEnumMediaTypes *pEnumMT ;
AM_MEDIA_TYPE mtIn ;
AM_MEDIA_TYPE mtOut ;
IPin *pPinIn ;
IPin *pPinOut ;
ULONG ul ;
DWORD dw ;
int iPos ;
BOOL bInOK = FALSE ; // initially
BOOL bOutOK = FALSE ; // initially
BOOL bFound = FALSE ; // initially
*pClsid = GUID_NULL ; // to start with
// First get the in and out pins' mediatypes
iPos = 0 ;
do { // for Input pin
hr = FindMatchingPin(pFilter, 0, PINDIR_INPUT, FALSE, iPos, &pPinIn) ; // want connected in pin
if (FAILED(hr) || NULL == pPinIn)
{
DbgLog((LOG_TRACE, 3,
TEXT("No connected In pin #%d for intermediate filter %S"),
iPos, lpszwName)) ;
return E_UNEXPECTED ; // no point trying anymore
}
pPinIn->ConnectionMediaType(&mtIn) ;
dw = GetStreamFromMediaType(&mtIn) ;
if (dwStream != dw)
{
DbgLog((LOG_TRACE, 3, TEXT("In pin %s is of stream type 0x%lx; looking for 0x%lx"),
(LPCTSTR) CDisp(pPinIn), dw, dwStream)) ;
FreeMediaType(mtIn) ;
}
else
{
DbgLog((LOG_TRACE, 3, TEXT("In pin %s matches required stream type 0x%lx"),
(LPCTSTR) CDisp(pPinIn), dwStream)) ;
bInOK = TRUE ;
}
pPinIn->Release() ; // don't need it anymore
iPos++ ; // try the next pin
} while (!bInOK) ;
// If we come here, we must have got a matching connected input pin
iPos = 0 ;
do { // for Output pin
hr = FindMatchingPin(pFilter, 0, PINDIR_OUTPUT, FALSE, iPos, &pPinOut) ; // want connected out pin
if (FAILED(hr) || NULL == pPinOut)
{
DbgLog((LOG_TRACE, 3,
TEXT("No connected Out pin #%d for intermediate filter %S"),
iPos, lpszwName)) ;
FreeMediaType(mtIn) ; // else we leak!!!
return E_UNEXPECTED ; // no point trying anymore
}
pPinOut->ConnectionMediaType(&mtOut) ;
dw = GetStreamFromMediaType(&mtOut) ;
if (dwStream != dw)
{
DbgLog((LOG_TRACE, 3, TEXT("Out pin %s is of stream type 0x%lx; looking for 0x%lx"),
(LPCTSTR) CDisp(pPinOut), dw, dwStream)) ;
FreeMediaType(mtOut) ;
}
else
{
DbgLog((LOG_TRACE, 3, TEXT("Out pin %s matches required stream type 0x%lx"),
(LPCTSTR) CDisp(pPinOut), dwStream)) ;
bOutOK = TRUE ;
}
pPinOut->Release() ; // don't need it anymore
iPos++ ; // try the next pin
} while (!bOutOK) ;
// If we come here, we must have got a matching connected output pin
// Get the filter enumerator based on the in and out mediatypes
hr = m_pMapper->EnumMatchingFilters(&pEnumFilters, MERIT_DO_NOT_USE+1,
TRUE, mtIn.majortype, mtIn.subtype,
FALSE, TRUE, mtOut.majortype, mtOut.subtype) ;
if (FAILED(hr) || NULL == pEnumFilters)
{
DbgLog((LOG_ERROR, 1, TEXT("ERROR: No matching filter enum found (Error 0x%lx)"), hr)) ;
FreeMediaType(mtIn) ;
FreeMediaType(mtOut) ;
return E_UNEXPECTED ;
}
// Now pick the right filter (we only have the "Name" to do the matching)
while (! bFound &&
S_OK == pEnumFilters->Next(1, &pRegFilter, &ul) && 1 == ul)
{
if (0 == lstrcmpW(pRegFilter->Name, lpszwName)) // we got a match!!!
{
DbgLog((LOG_TRACE, 3, TEXT("Found a matching registered filter for %S"), lpszwName)) ;
*pClsid = pRegFilter->Clsid ;
bFound = TRUE ;
}
CoTaskMemFree(pRegFilter) ; // done with this filter's info
}
// Now release everything (whether we got anything or not)
pEnumFilters->Release() ;
FreeMediaType(mtIn) ;
FreeMediaType(mtOut) ;
return bFound ? S_OK : E_FAIL ;
}
HRESULT CDvdGraphBuilder::RenderIntermediateOutPin(IBaseFilter *pFilter, DWORD dwStream,
AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::RenderIntermediateOutPin(0x%lx, 0x%lx, 0x%lx)"),
pFilter, dwStream, pStatus)) ;
HRESULT hr ;
IPin *pPinOut ;
IPin *apPinOutDec[MAX_DEC_OUT_PINS + 1] ; // 1 for terminating NULL
IPin *pPinIn ;
ULONG ul ;
DWORD dwDecFlag ;
HRESULT hrFinal = S_OK ;
BOOL bConnected ;
while (SUCCEEDED(hr = FindMatchingPin(pFilter, dwStream, PINDIR_OUTPUT, TRUE, 0, &pPinOut)))
{
DbgLog((LOG_TRACE, 3, TEXT("Open out pin %s found on intermediate filter"),
(LPCTSTR) CDisp(pPinOut))) ;
ResetPinInterface(apPinOutDec, NUMELMS(apPinOutDec)) ; // set i/f ptrs to NULL
dwDecFlag = AM_DVD_SWDEC_PREFER ; // we intentionally prefer SWDEC here
hr = DecodeDVDStream(pPinOut, dwStream, &dwDecFlag, pStatus, apPinOutDec) ;
if (SUCCEEDED(hr) && apPinOutDec[0]) // first element is good enough
{
DbgLog((LOG_TRACE, 3, TEXT("Out pin %s is %s decoded (to out pin %s) (stream 0x%lx)"),
(LPCTSTR) CDisp(pPinOut), AM_DVD_SWDEC_ONLY == dwDecFlag ? TEXT("SW") : TEXT("HW"),
(LPCTSTR) CDisp(apPinOutDec[0]), dwStream)) ;
switch (dwStream)
{
case AM_DVD_STREAM_VIDEO:
DbgLog((LOG_TRACE, 5, TEXT("Going to render intermediate filter's additional 'Video' stream"))) ;
// So far I don't know of anyone coming here. But IBM's stuff
// goes to the audio case. So I am not ignoring the video case,
// just in case someone is that much insane!!!
//
// Only if we have been able to render primary video...
if (0 == (pStatus->dwFailedStreamsFlag & AM_DVD_STREAM_VIDEO))
{
pPinIn = NULL ;
hr = E_FAIL ; // assume we'll fail
if (m_pOvM) // ... using OvMixer
{
hr = FindMatchingPin(m_pOvM, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ;
}
else if (m_pVMR) // ... using VMR
{
hr = FindMatchingPin(m_pVMR, 0, PINDIR_INPUT, TRUE, 0, &pPinIn) ;
}
// ASSERT(SUCCEEDED(hr) && pPinIn) ;
if (SUCCEEDED(hr) && pPinIn)
{
bConnected = FALSE ; // reset flag every time
int i = 0 ;
while (!bConnected && i < MAX_DEC_OUT_PINS && apPinOutDec[i])
{
hr = ConnectPins(apPinOutDec[i], pPinIn, AM_DVD_CONNECT_DIRECTFIRST) ;
if (FAILED(hr)) // what?!?
{
DbgLog((LOG_TRACE, 1, TEXT("Pin %s (#%ld) did NOT connect to pin %s"),
(LPCTSTR)CDisp(apPinOutDec[i]), i, (LPCTSTR)CDisp(pPinIn))) ;
i++ ;
ASSERT(i <= MAX_DEC_OUT_PINS) ;
}
else
{
DbgLog((LOG_TRACE, 5, TEXT("Pin %s connected to pin %s"),
(LPCTSTR)CDisp(apPinOutDec[i]), (LPCTSTR)CDisp(pPinIn))) ;
// Intentionally ignoring any intermediate filters coming in here -- I am tired
// EnumFiltersBetweenPins(dwStream, pPinOut, pPinIn, pStatus) ;
bConnected = TRUE ;
}
} // end of while (!bConnected ...)
if (!bConnected)
{
DbgLog((LOG_TRACE, 3, TEXT("Couldn't connect any of the %d intermediate video out pins"), i)) ;
hrFinal = hr ; // last error is good enough
}
pPinIn->Release() ; // done with the pin
}
} // end of if (0 == (pStatus->dwFailedStreamsFlag ...))
else
{
ASSERT(FALSE) ; // so that we know about it
DbgLog((LOG_TRACE, 5, TEXT("OvM/VMR is not usable. Will skip rendering this stream."))) ;
hrFinal = E_UNEXPECTED ;
}
break ;
case AM_DVD_STREAM_AUDIO:
DbgLog((LOG_TRACE, 5, TEXT("Going to render intermediate filter's additional 'Audio' stream"))) ;
hr = RenderDecodedAudio(apPinOutDec, pStatus) ;
if (FAILED(hr))
{
DbgLog((LOG_TRACE, 3, TEXT("Couldn't connect the intermediate audio out pin"))) ;
hrFinal = hr ;
}
else
DbgLog((LOG_TRACE, 5, TEXT("XXX's SW AC3 must have been rendered now"))) ; // XXX = IBM
break ;
case AM_DVD_STREAM_SUBPIC:
DbgLog((LOG_TRACE, 5, TEXT("Skip rendering intermediate filter's additional 'Subpicture' stream"))) ;
// hr = RenderDecodedSubpic(apPinOutDec, pStatus) ;
ASSERT(FALSE) ; // not expected here at all
break ;
case AM_DVD_STREAM_LINE21:
DbgLog((LOG_TRACE, 5, TEXT("Skip rendering intermediate filter's additional 'CC' stream"))) ;
// hr = RenderLine21Stream(apPinOutDec[0], pStatus) ; -- hopefully only one L21 out pin
ASSERT(FALSE) ; // not expected here at all
break ;
} // end of switch()
ReleasePinInterface(apPinOutDec) ; // done with the decoded out pin(s)
} // end of if (SUCCEEDED(hr) && apPinOutDec[0])
else
DbgLog((LOG_TRACE, 1, TEXT("Intermediate out pin %s could NOT decoded (stream 0x%lx)"),
(LPCTSTR) CDisp(pPinOut), dwStream)) ;
pPinOut->Release() ; // done with the pin
} // end of while ()
return hrFinal ;
}
HRESULT CDvdGraphBuilder::EnumFiltersBetweenPins(DWORD dwStream, IPin *pPinOut, IPin *pPinIn,
AM_DVD_RENDERSTATUS *pStatus)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::EnumFiltersBetweenPins(0x%lx, Out=%s, In=%s, 0x%lx)"),
dwStream, (LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn), pStatus)) ;
if (NULL == pPinOut || NULL == pPinIn) // what!!!
return E_UNEXPECTED ;
GUID Clsid ;
int iCount = 0 ;
PIN_INFO pi ;
FILTER_INFO fi ;
IEnumPins *pEnumPins ;
IBaseFilter *pFilter = NULL ;
IPin *pPinIn2 = NULL ; // init so that we don't have junk
IPin *pPinOut2 = NULL ; // init so that we don't have junk
HRESULT hr = pPinIn->ConnectedTo(&pPinOut2) ;
while (SUCCEEDED(hr) && pPinOut2 && !IsEqualObject(pPinOut, pPinOut2))
{
pPinOut2->QueryPinInfo(&pi) ;
pFilter = pi.pFilter ;
ASSERT(pFilter && PINDIR_OUTPUT == pi.dir) ;
//
// We intentionally keep the extra ref count because this is an intermediate
// filter and other intermediate filters picked up through registry based
// filter enum (for SW decoding case) will have the extra ref count. We
// release the IBaseFilter interface pointer in CListFilters::ClearList() or
// CListFilters::RemoveAllFromGraph() and if we don't keep this extra ref
// count here, we'll fault. On the other hand we must do Release() on
// CListFilters elements, because SW enum-ed filters will not otherwise be
// unloaded.
//
// if (pi.pFilter)
// pi.pFilter->Release() ; // it has an extra ref count from QueryPinInfo()
pFilter->QueryFilterInfo(&fi) ;
if (! m_ListFilters.IsInList(pFilter) ) // not yet in list
{
hr = GetFilterCLSID(pFilter, dwStream, fi.achName, &Clsid) ;
ASSERT(SUCCEEDED(hr)) ;
m_ListFilters.AddFilter(pFilter, NULL /* fi.achName */, &Clsid) ; // presumably it's a SW filter
DbgLog((LOG_TRACE, 5, TEXT("Intermediate filter %S added to our list"), fi.achName)) ;
}
else
DbgLog((LOG_TRACE, 5, TEXT("Intermediate filter %S is already in our list"), fi.achName)) ;
fi.pGraph->Release() ; // else we leak!!
pPinOut2->Release() ; // done with the pin for now
pPinOut2 = NULL ;
iCount++ ;
// Check for any open out pin on the intermediate filter. We may find
// one such on IBM's CSS filter for the SW AC3 decoder.
hr = RenderIntermediateOutPin(pFilter, dwStream, pStatus) ;
if (FAILED(hr))
{
DbgLog((LOG_TRACE, 3,
TEXT("Failed to render intermediate filter's open out pin of type 0x%lx (Error 0x%lx)"),
dwStream, hr)) ;
ASSERT(FALSE) ; // so that we know of this weird case
}
// Now get the (stream-matching) input pin of this filter to traverse the chain
hr = FindMatchingPin(pFilter, dwStream, PINDIR_INPUT, FALSE, 0, &pPinIn2) ; // want connected pin
if (FAILED(hr) || NULL == pPinIn2)
{
DbgLog((LOG_ERROR, 1, TEXT("Filter %S does NOT have any connected pin of type 0x%lx"),
fi.achName, dwStream)) ;
ASSERT(pPinIn2) ;
DbgLog((LOG_TRACE, 5, TEXT("(Incomplete) %d filter(s) found between pin %s and pin %s"),
iCount, (LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
return hr ; // we are hopefully not leaking anything
}
hr = pPinIn2->ConnectedTo(&pPinOut2) ;
pPinIn2->Release() ; // done with this in pin
} // end of while () loop
if (pPinOut2) // if valid IPin interface
pPinOut2->Release() ; // release it
DbgLog((LOG_TRACE, 5, TEXT("Total %d filter(s) found between pin %s and pin %s"),
iCount, (LPCTSTR)CDisp(pPinOut), (LPCTSTR)CDisp(pPinIn))) ;
return S_OK ; // successfuly done
}
void CDvdGraphBuilder::CheckDDrawExclMode(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::CheckDDrawExclMode()"))) ;
HRESULT hr ;
#if 0
hr = EnsureOverlayMixerExists() ;
if (FAILED(hr))
{
return ;
}
ASSERT(m_pOvM) ;
#endif // #if 0
// If OvMixer has already been created, it's most probably by a query for
// the DDraw (non-)exclusive mode interfaces. Otherwise the app doesn't
// want to use those interfaces, and hence it does NOT need OvMixer.
if (NULL == m_pOvM)
{
DbgLog((LOG_TRACE, 5,
TEXT("CheckDDrawExclMode(): OverlayMixer does NOT exist => no excl mode."))) ;
m_bDDrawExclMode = FALSE ;
return ;
}
// Get the IDDrawExclModeVideo interface
IDDrawExclModeVideo *pDDXMV ;
hr = m_pOvM->QueryInterface(IID_IDDrawExclModeVideo, (LPVOID *) &pDDXMV) ;
if (FAILED(hr) || NULL == pDDXMV)
{
DbgLog((LOG_ERROR, 1,
TEXT("WARNING: Can't get IDDrawExclModeVideo on OverlayMixer (Error 0x%lx)"), hr)) ;
return ;
}
// Get the DDraw object and surface info from OverlayMixer (and release too)
IDirectDraw *pDDObj ;
IDirectDrawSurface *pDDSurface ;
BOOL bExtDDObj ;
BOOL bExtDDSurface ;
hr = pDDXMV->GetDDrawObject(&pDDObj, &bExtDDObj) ;
ASSERT(SUCCEEDED(hr)) ;
hr = pDDXMV->GetDDrawSurface(&pDDSurface, &bExtDDSurface) ;
ASSERT(SUCCEEDED(hr)) ;
if (pDDObj)
pDDObj->Release() ;
if (pDDSurface)
pDDSurface->Release() ;
pDDXMV->Release() ; // release before returning
// Both true means we are really in excl mode
m_bDDrawExclMode = bExtDDObj && bExtDDSurface ;
}
IPin * CDvdGraphBuilder::GetFilterForMediaType(DWORD dwStream, AM_MEDIA_TYPE *pmt, IBaseFilter *pOutFilter)
{
DbgLog((LOG_TRACE, 4, TEXT("CDvdGraphBuilder::GetFilterForMediaType(0x%lx, 0x%lx, 0x%lx)"),
dwStream, pmt, pOutFilter)) ;
IBaseFilter *pInFilter ;
IPin *pPinIn ;
LPWSTR lpszwName ;
HRESULT hr ;
for (int i = 0 ; i < m_ListFilters.GetCount() ; i++)
{
// I could have checked if the filter from list is a HW filter, but I decided not to.
m_ListFilters.GetFilter(i, &pInFilter, &lpszwName) ;
// Don't want to connect to the out pin's filter's in pin (cyclic graph)
if (pOutFilter && IsEqualObject(pOutFilter, pInFilter))
continue ;
hr = FindMatchingPin(pInFilter, dwStream, PINDIR_INPUT, TRUE, 0, &pPinIn) ;
if (SUCCEEDED(hr) && pPinIn)
{
hr = pPinIn->QueryAccept(pmt) ;
if (SUCCEEDED(hr)) // input pin seems to accept mediatype
{
DbgLog((LOG_TRACE, 5, TEXT("Input pin %s of type %d matches mediatype"),
(LPCTSTR)CDisp(pPinIn), dwStream)) ;
return pPinIn ; // return matching in pin
}
// Otherwise not a matching mediatype -- skip this one.
DbgLog((LOG_TRACE, 5, TEXT("Input pin %s of type %d didn't like mediatype"),
(LPCTSTR)CDisp(pPinIn), dwStream)) ;
pPinIn->Release() ;
pPinIn = NULL ;
}
else
DbgLog((LOG_TRACE, 5, TEXT("No open input pin of type %d found on %S"),
dwStream, lpszwName)) ;
} // end of for (i)
return NULL ; // didn't match any
}
// ---------------------------------------------
// Implementation of the CListFilters class...
// ---------------------------------------------
CListFilters::CListFilters(int iMax /* = FILTERLIST_DEFAULT_MAX*/ ,
int iInc /* = FILTERLIST_DEFAULT_INC */)
{
DbgLog((LOG_TRACE, 4, TEXT("CListFilters::CListFilters(%d, %d)"), iMax, iInc)) ;
m_iCount = 0 ;
m_iMax = iMax ;
m_iInc = iInc ;
m_pGraph = NULL ;
m_pFilters = new CFilterData [m_iMax] ;
ASSERT(m_pFilters) ;
}
CListFilters::~CListFilters(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CListFilters::~CListFilters()"))) ;
if (m_pFilters)
delete [] m_pFilters ;
m_iCount = 0 ;
}
void CListFilters::RemoveAllFromGraph(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CListFilters::RemoveAllFromGraph()"))) ;
IBaseFilter *pFilter ;
for (int i = 0 ; i < m_iCount ; i++)
{
if (pFilter = m_pFilters[i].GetInterface())
{
#ifdef DEBUG
FILTER_INFO fi ;
pFilter->QueryFilterInfo(&fi) ;
DbgLog((LOG_TRACE, 5, TEXT("Removing filter %S..."), fi.achName)) ;
if (fi.pGraph)
fi.pGraph->Release() ;
#endif // DEBUG
EXECUTE_ASSERT(SUCCEEDED(m_pGraph->RemoveFilter(pFilter))) ;
// pFilter->Release() ; -- done in ResetElement() below
m_pFilters[i].ResetElement() ;
}
}
m_iCount = 0 ;
m_pGraph = NULL ; // no filter in list, why have the graph??
}
BOOL CListFilters::AddFilter(IBaseFilter *pFilter, LPCWSTR lpszwName, GUID *pClsid)
{
DbgLog((LOG_TRACE, 4, TEXT("CListFilters::AddFilter(0x%lx, %S, 0x%lx)"),
pFilter, lpszwName ? lpszwName : L"NULL", pClsid)) ;
if (NULL == pFilter)
{
DbgLog((LOG_ERROR, 1, TEXT("Internal Error: NULL pFilter param passed to AddFilter()"))) ;
return FALSE ;
}
if (m_iCount >= m_iMax)
{
if (! ExpandList() ) // couldn't expand list
{
DbgLog((LOG_ERROR, 1, TEXT("INTERNAL ERROR: Too many filters added to CListFilters"))) ;
return FALSE ;
}
DbgLog((LOG_TRACE, 5, TEXT("CListFilters list has been extended"))) ;
}
m_pFilters[m_iCount].SetElement(pFilter, lpszwName, pClsid) ;
m_iCount++ ;
return TRUE ;
}
BOOL CListFilters::GetFilter(int iIndex, IBaseFilter **ppFilter, LPWSTR *lpszwName)
{
DbgLog((LOG_TRACE, 4, TEXT("CListFilters::GetFilter(%d, 0x%lx, 0x%lx)"),
iIndex, ppFilter, lpszwName)) ;
if (iIndex > m_iCount)
{
DbgLog((LOG_ERROR, 1,
TEXT("INTERNAL ERROR: Bad index (%d) for CListDecoders::GetFilter()"), iIndex)) ;
*ppFilter = NULL ;
return FALSE ;
}
*ppFilter = m_pFilters[iIndex].GetInterface() ;
*lpszwName = m_pFilters[iIndex].GetName() ;
return TRUE ;
}
BOOL CListFilters::GetFilter(GUID *pClsid, int iIndex, IBaseFilter **ppFilter)
{
DbgLog((LOG_TRACE, 4, TEXT("CListFilters::GetFilter(0x%lx, %d, 0x%lx)"),
pClsid, iIndex, ppFilter)) ;
GUID *pFilterClsid ;
for (int i = 0 ; i < m_iCount ; i++)
{
if ((pFilterClsid = m_pFilters[i].GetClsid()) &&
IsEqualGUID(*pClsid, *pFilterClsid))
{
if (0 == iIndex)
{
*ppFilter = m_pFilters[i].GetInterface() ;
return TRUE ;
}
else // skip this one -- we want a later one
iIndex-- ;
}
}
*ppFilter = NULL ;
return FALSE ;
}
BOOL CListFilters::IsInList(IBaseFilter *pFilter)
{
DbgLog((LOG_TRACE, 4, TEXT("CListFilters::IsInList(0x%lx)"), pFilter)) ;
for (int i = 0 ; i < m_iCount ; i++)
{
if (IsEqualObject(pFilter, m_pFilters[i].GetInterface()))
return TRUE ;
}
return FALSE ; // didn't match any
}
void CListFilters::ClearList(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CListFilters::ClearList()"))) ;
for (int i = 0 ; i < m_iCount ; i++)
{
#ifdef DEBUG
FILTER_INFO fi ;
m_pFilters[i].GetInterface()->QueryFilterInfo(&fi) ;
DbgLog((LOG_TRACE, 5, TEXT("Removing filter %S..."), fi.achName)) ;
if (fi.pGraph)
fi.pGraph->Release() ;
#endif // DEBUG
m_pFilters[i].ResetElement() ;
}
m_iCount = 0 ;
}
BOOL CListFilters::ExpandList(void)
{
return FALSE ; // not implemented for now
}
// -------------------------------------
// CFilterData class implementation...
// -------------------------------------
CFilterData::CFilterData(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CFilterData::CFilterData()"))) ;
m_pFilter = NULL ;
m_lpszwName = NULL ;
m_pClsid = NULL ;
}
CFilterData::~CFilterData(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CFilterData::~CFilterData()"))) ;
ResetElement() ;
}
void CFilterData::SetElement(IBaseFilter *pFilter, LPCWSTR lpszwName, GUID *pClsid)
{
DbgLog((LOG_TRACE, 4, TEXT("CFilterData::SetElement(0x%lx, 0x%lx, 0x%lx)"),
pFilter, lpszwName, pClsid)) ;
m_pFilter = pFilter ; // should we AddRef() too?
if (lpszwName)
{
m_lpszwName = new WCHAR [sizeof(WCHAR) * (lstrlenW(lpszwName) + 1)] ;
ASSERT(m_lpszwName) ;
if (NULL == m_lpszwName) // bad situation...
return ; // ...just bail out
lstrcpyW(m_lpszwName, lpszwName) ;
}
if (pClsid)
{
m_pClsid = (GUID *) new BYTE[sizeof(GUID)] ;
ASSERT(m_pClsid) ;
if (NULL == m_pClsid) // bad situation...
return ; // ...just bail out
*m_pClsid = *pClsid ;
}
}
void CFilterData::ResetElement(void)
{
DbgLog((LOG_TRACE, 4, TEXT("CFilterData::ResetElement()"))) ;
LONG l ;
if (m_pFilter)
{
l = m_pFilter->Release() ;
DbgLog((LOG_TRACE, 3, TEXT("post Release() ref count is %ld"), l)) ;
m_pFilter = NULL ;
}
if (m_lpszwName)
{
DbgLog((LOG_TRACE, 5, TEXT("Filter %S has just been released"), m_lpszwName)) ;
delete [] m_lpszwName ;
m_lpszwName = NULL ;
}
if (m_pClsid)
{
delete [] ((BYTE *) m_pClsid) ;
m_pClsid = NULL ;
}
}