// Copyright (c) 1994 - 2000 Microsoft Corporation. All Rights Reserved. #include #include #ifdef FILTER_DLL // define the GUIDs for streams and my CLSID in this file #include #endif #include #include #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 ; } }