/************************************************************************** * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR * PURPOSE. * * Copyright (c) 1993 - 1998 Microsoft Corporation. All Rights Reserved. * **************************************************************************/ // // DVDPlay.cpp: DVD Playback class // #include #include #include "mpconfig.h" #include "VidPlay.h" // // CBaseVideoPlayer constructor // CBaseVideoPlayer::CBaseVideoPlayer() { m_eState = Uninitialized ; ZeroMemory(m_achFileName, sizeof(m_achFileName)) ; m_pGraph = NULL ; m_pMC = NULL ; m_pME = NULL ; m_dwColorKey = 253 ; // magenta by default } // // CBaseVideoPlayer destructor // CBaseVideoPlayer::~CBaseVideoPlayer() { ReleaseInterfaces() ; } // // CBaseVideoPlayer::GetInterfaces(): Gets the standard interfaces required for // playing back through a filter graph // HRESULT CBaseVideoPlayer::GetInterfaces(HWND hWndApp) { HRESULT hr ; hr = m_pGraph->QueryInterface(IID_IMediaControl, (LPVOID *)&m_pMC) ; ASSERT(SUCCEEDED(hr) && m_pMC) ; hr = m_pGraph->QueryInterface(IID_IMediaEventEx, (LPVOID *)&m_pME) ; ASSERT(SUCCEEDED(hr) && m_pME) ; // // Also set up the event notification so that the main window gets // informed about all that we care about during playback. // hr = m_pME->SetNotifyWindow((OAHWND) hWndApp, WM_PLAY_EVENT, (ULONG)(LPVOID)m_pME) ; ASSERT(SUCCEEDED(hr)) ; m_eState = Stopped ; // graph has been built return S_OK ; } // // CBaseVideoPlayer::ReleaseInterfaces(): Let go of the standard interfaces taken for playback. // void CBaseVideoPlayer::ReleaseInterfaces(void) { DbgLog((LOG_TRACE, 5, TEXT("CBaseVideoPlayer::ReleaseInterfaces() entered"))) ; if (m_pMC) { m_pMC->Release() ; m_pMC = NULL ; } if (m_pME) { // clear any already set notification arrangement m_pME->SetNotifyWindow(NULL, WM_PLAY_EVENT, (ULONG)(LPVOID)m_pME) ; m_pME->Release() ; m_pME = NULL ; } m_eState = Uninitialized ; // no graph available } // // CBaseVideoPlayer::WaitForState(): Wait for the state to change to the given one. // void CBaseVideoPlayer::WaitForState(FILTER_STATE State) { // Make sure we have switched to the required state LONG lfs ; do { m_pMC->GetState(10, &lfs) ; } while (State != lfs) ; } // // CBaseVideoPlayer::Play(): Plays the filter graph (and waits to really start playing) // BOOL CBaseVideoPlayer::Play(void) { DbgLog((LOG_TRACE, 5, TEXT("CBaseVideoPlayer::Play() entered"))) ; if (! IsGraphReady() ) { DbgLog((LOG_TRACE, 1, TEXT("DVD-Video playback graph hasn't been built yet"))) ; return FALSE ; } HRESULT hr = m_pMC->Run() ; if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("WARNING: IMediaControl::Run() failed (Error 0x%lx)"), hr)) ; return FALSE ; } WaitForState(State_Running) ; // Some state changes now m_eState = Playing ; return TRUE ; // success } // // CBaseVideoPlayer::Pause(): Pauses/Cues the filter graph (and waits to really pause) // BOOL CBaseVideoPlayer::Pause(void) { DbgLog((LOG_TRACE, 5, TEXT("CBaseVideoPlayer::Pause() entered"))) ; if (! IsGraphReady() ) { DbgLog((LOG_TRACE, 1, TEXT("DVD-Video playback graph hasn't been built yet"))) ; return FALSE ; } HRESULT hr = m_pMC->Pause() ; if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("WARNING: IMediaControl::Pause() failed (Error 0x%lx)"), hr)) ; return FALSE ; } WaitForState(State_Paused) ; // Some state changes now m_eState = Paused ; return TRUE ; // success } // // CBaseVideoPlayer::Stop(): Stops the filter graph (and waits to really stop) // BOOL CBaseVideoPlayer::Stop(void) { DbgLog((LOG_TRACE, 5, TEXT("CBaseVideoPlayer::Stop() entered"))) ; if (! IsGraphReady() ) { DbgLog((LOG_TRACE, 1, TEXT("DVD-Video playback graph hasn't been built yet"))) ; return FALSE ; } HRESULT hr = m_pMC->Stop() ; if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("WARNING: IMediaControl::Stop() failed (Error 0x%lx)"), hr)) ; return FALSE ; } WaitForState(State_Stopped) ; // Some state changes now m_eState = Stopped ; return TRUE ; // success } // // CBaseVideoPlayer::GetColorKey(): Returns color key to the app so that other // components (here the DDraw object) can be told about it. // HRESULT CBaseVideoPlayer::GetColorKey(DWORD *pdwColorKey) { DbgLog((LOG_TRACE, 5, TEXT("CBaseVideoPlayer::GetColorKey() entered"))) ; if (! IsGraphReady() ) return E_UNEXPECTED ; *pdwColorKey = m_dwColorKey ; // use the cached color key info return S_OK ; } // // CDVDPlayer constructor // CDVDPlayer::CDVDPlayer() { DbgLog((LOG_TRACE, 3, TEXT("CDVDPlayer c-tor entered"))) ; m_pDvdGB = NULL ; m_pDvdC = NULL ; m_pDvdI = NULL ; } // // CDVDPlayer destructor // CDVDPlayer::~CDVDPlayer() { DbgLog((LOG_TRACE, 3, TEXT("CDVDPlayer d-tor entered"))) ; ReleaseInterfaces() ; if (m_pDvdGB) m_pDvdGB->Release() ; DbgLog((LOG_TRACE, 3, TEXT("CDVDPlayer d-tor exiting..."))) ; } // // CDVDPlayer::Initialize(): Creates a DVD graph builder object for later building // a filter graph. // BOOL CDVDPlayer::Initialize(void) { DbgLog((LOG_TRACE, 5, TEXT("CDVDPlayer::Initialize() entered"))) ; // // Now instantiate the DVD Graph Builder object and start working // HRESULT hr = CoCreateInstance(CLSID_DvdGraphBuilder, NULL, CLSCTX_INPROC, IID_IDvdGraphBuilder, (LPVOID *)&m_pDvdGB) ; if (FAILED(hr) || NULL == m_pDvdGB) { DbgLog((LOG_ERROR, 0, TEXT("ERROR: DirectShow DVD software not installed properly (Error 0x%lx)"), hr)) ; return FALSE ; } return TRUE; } // // CDVDPlayer::BuildGraph(): Builds a filter graph for playing back the specified // DVD title/file. Also gets some interfaces that are required for controlling // playback. // HRESULT CDVDPlayer::BuildGraph(HWND hWndApp, LPDIRECTDRAW pDDObj, LPDIRECTDRAWSURFACE pDDPrimary) { DbgLog((LOG_TRACE, 5, TEXT("CDVDPlayer::BuildGraph() entered"))) ; HRESULT hr ; // First release any existing interface pointer(s) ReleaseInterfaces() ; SetColorKey(253) ; // default magenta for 8bpp // Check if a DVD-Video volume name has been specified; if so, use that WCHAR achwFileName[MAX_PATH] ; LPCWSTR lpszwFileName = NULL ; // by default if (lstrlen(GetFileName()) > 0) // if something was specified before { #ifdef UNICODE lstrcpy(achwFileName, GetFileName()) ; #else MultiByteToWideChar(CP_ACP, 0, GetFileName(), -1, achwFileName, MAX_PATH) ; #endif // UNICODE lpszwFileName = achwFileName ; } DbgLog((LOG_TRACE, 5, TEXT("DVD file <%s> will be played"), GetFileName())) ; // Set DDraw object and surface on DVD graph builder before starting to build graph IDDrawExclModeVideo *pDDXMV ; hr = m_pDvdGB->GetDvdInterface(IID_IDDrawExclModeVideo, (LPVOID *)&pDDXMV) ; if (FAILED(hr) || NULL == pDDXMV) { DbgLog((LOG_ERROR, 0, TEXT("ERROR: IDvdGB::GetDvdInterface(IDDrawExclModeVideo) failed (Error 0x%lx)"), hr)) ; return hr ; } hr = pDDXMV->SetDDrawObject(pDDObj) ; if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("ERROR: IDDrawExclModeVideo::SetDDrawObject() failed (Error 0x%lx)"), hr)) ; pDDXMV->Release() ; // release before returning return hr ; } hr = pDDXMV->SetDDrawSurface(pDDPrimary) ; if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("ERROR: IDDrawExclModeVideo::SetDDrawSurface() failed (Error 0x%lx)"), hr)) ; pDDXMV->SetDDrawObject(NULL) ; // to reset pDDXMV->Release() ; // release before returning return hr ; } pDDXMV->Release() ; // done with the interface // Build the graph AM_DVD_RENDERSTATUS Status ; TCHAR achBuffer[1000] ; hr = m_pDvdGB->RenderDvdVideoVolume(lpszwFileName, AM_DVD_HWDEC_PREFER, &Status) ; if (FAILED(hr)) { AMGetErrorText(hr, achBuffer, sizeof(achBuffer)) ; MessageBox(hWndApp, achBuffer, "Error", MB_OK) ; return hr ; } if (S_FALSE == hr) // if partial success { if (0 == GetStatusText(&Status, achBuffer, sizeof(achBuffer))) { lstrcpy(achBuffer, TEXT("An unknown error has occurred")) ; } lstrcat(achBuffer, TEXT("\n\nDo you still want to continue?")) ; if (IDNO == MessageBox(hWndApp, achBuffer, TEXT("Warning"), MB_YESNO)) { return E_FAIL ; } } GetInterfaces(hWndApp) ; hr = GetColorKeyInternal() ; ASSERT(SUCCEEDED(hr)) ; return S_OK ; } // // CDVDPlayer::GetInterfaces(): Gets some interfaces useful for DVD title playback // HRESULT CDVDPlayer::GetInterfaces(HWND hWndApp) { DbgLog((LOG_TRACE, 5, TEXT("CDVDPlayer::GetInterfaces() entered"))) ; HRESULT hr ; hr = m_pDvdGB->GetFiltergraph(&m_pGraph) ; ASSERT(SUCCEEDED(hr) && m_pGraph) ; // Now get the DVD-specific interfaces (though we don't use them in this app). hr = m_pDvdGB->GetDvdInterface(IID_IDvdControl, (LPVOID *)&m_pDvdC) ; ASSERT(SUCCEEDED(hr) && m_pDvdC) ; hr = m_pDvdGB->GetDvdInterface(IID_IDvdInfo, (LPVOID *)&m_pDvdI) ; ASSERT(SUCCEEDED(hr) && m_pDvdI) ; return CBaseVideoPlayer::GetInterfaces(hWndApp) ; // get the standard interfaces } // // CDVDPlayer::ClearGraph(): Tears down the filter graph created by BuildGraph() // and also releases the DVD graph builder object. // HRESULT CDVDPlayer::ClearGraph() { DbgLog((LOG_TRACE, 5, TEXT("CDVDPlayer::ClearGraph() entered"))) ; ReleaseInterfaces() ; // let go of the other interfaces if (m_pDvdGB) { m_pDvdGB->Release() ; m_pDvdGB = NULL ; } return S_OK ; } // // CDVDPlayer::GetColorKeyInternal(): Retrieves color key and stores it for later // queries from the app. // HRESULT CDVDPlayer::GetColorKeyInternal(IBaseFilter *pOvM /* = NULL */) { ASSERT(NULL == pOvM) ; // we don't need pOvM passed in // Get color key from MixerPinConfig interface via IDvdGraphBuilder IMixerPinConfig2 *pMPC ; DWORD dwColorKey ; HRESULT hr = m_pDvdGB->GetDvdInterface(IID_IMixerPinConfig2, (LPVOID *) &pMPC) ; if (SUCCEEDED(hr)) { hr = pMPC->GetColorKey(NULL, &dwColorKey) ; ASSERT(SUCCEEDED(hr)) ; SetColorKey(dwColorKey) ; // Set mode to stretch - that way we don't fight the overlay // mixer about the exact way to fix the aspect ratio pMPC->SetAspectRatioMode(AM_ARMODE_STRETCHED); pMPC->Release() ; } else { DbgLog((LOG_ERROR, 0, TEXT("WARNING: IDvdGraphBuilder::GetDvdInterface(IID_IMixerPinConfig2) failed (Error 0x%lx)"), hr)) ; return hr ; } return S_OK ; } // // CDVDPlayer::GetNativeVideoData(): Retrieves the original video size from OverlayMixer's // IDDrawExclModeVideo interface. // HRESULT CDVDPlayer::GetNativeVideoData(DWORD *pdwWidth, DWORD *pdwHeight, DWORD *pdwARX, DWORD *pdwARY) { DbgLog((LOG_TRACE, 5, TEXT("CDVDPlayer::GetNativeVideoData() entered"))) ; IDDrawExclModeVideo *pDDXMV ; HRESULT hr = m_pDvdGB->GetDvdInterface(IID_IDDrawExclModeVideo, (LPVOID *)&pDDXMV) ; if (FAILED(hr) || NULL == pDDXMV) { DbgLog((LOG_ERROR, 0, TEXT("ERROR: IDvdGB::GetDvdInterface(IDDrawExclModeVideo) failed (Error 0x%lx)"), hr)) ; return hr ; } hr = pDDXMV->GetNativeVideoProps(pdwWidth, pdwHeight, pdwARX, pdwARY) ; if (FAILED(hr)) { DbgLog((LOG_TRACE, 1, TEXT("WARNING: IDDrawExclModeVideo::GetNativeVideoProps() failed (Error 0x%lx)"), hr)) ; } pDDXMV->Release() ; // release before returning return hr ; } // // CDVDPlayer::SetVideoPosition(): Sets the source and destination rects on the // OverlayMixer's IDDrawExclModeVideo interface . // HRESULT CDVDPlayer::SetVideoPosition(DWORD dwLeft, DWORD dwTop, DWORD dwWidth, DWORD dwHeight) { DbgLog((LOG_TRACE, 5, TEXT("CDVDPlayer::SetVideoPosition() entered"))) ; IDDrawExclModeVideo *pDDXMV ; HRESULT hr = m_pDvdGB->GetDvdInterface(IID_IDDrawExclModeVideo, (LPVOID *)&pDDXMV) ; if (FAILED(hr) || NULL == pDDXMV) { DbgLog((LOG_ERROR, 0, TEXT("ERROR: IDvdGB::GetDvdInterface(IDDrawExclModeVideo) failed (Error 0x%lx)"), hr)) ; return hr ; } RECT rectDest ; SetRect(&rectDest, dwLeft, dwTop, dwLeft + dwWidth, dwTop + dwHeight) ; // NULL or (0, 0, 10000, 10000) for full video hr = pDDXMV->SetDrawParameters(NULL, &rectDest) ; if (FAILED(hr)) { DbgLog((LOG_TRACE, 1, TEXT("WARNING: IDDrawExclModeVideo::SetDrawParameters() failed (Error 0x%lx)"), hr)) ; } pDDXMV->Release() ; // release before returning return hr ; } // // CDVDPlayer::SetOverlayCallback(): Specify the pointer to the overlay notification // callback object which the OverlayMixer will call during playback. // HRESULT CDVDPlayer::SetOverlayCallback(IDDrawExclModeVideoCallback *pCallback) { DbgLog((LOG_TRACE, 5, TEXT("CDVDPlayer::SetOverlayCallback() entered"))) ; HRESULT hr ; IDDrawExclModeVideo *pDDXMV ; hr = m_pDvdGB->GetDvdInterface(IID_IDDrawExclModeVideo, (LPVOID *)&pDDXMV) ; if (FAILED(hr) || NULL == pDDXMV) { DbgLog((LOG_ERROR, 0, TEXT("ERROR: IDvdGB::GetDvdInterface(IDDrawExclModeVideo) failed (Error 0x%lx)"), hr)) ; return hr ; } hr = pDDXMV->SetCallbackInterface(pCallback, 0) ; if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("ERROR: IDDrawExclModeVideo::SetCallbackInterface() failed (Error 0x%lx)"), hr)) ; } pDDXMV->Release() ; // done with the interface return hr ; } // // CDVDPlayer::ReleaseInterfaces(): Let go of the interfaces taken in BuildGraph() // void CDVDPlayer::ReleaseInterfaces(void) { DbgLog((LOG_TRACE, 5, TEXT("CDVDPlayer::ReleaseInterfaces() entered"))) ; if (m_pDvdC) { m_pDvdC->Release() ; m_pDvdC = NULL ; } if (m_pDvdI) { m_pDvdI->Release() ; m_pDvdI = NULL ; } if (m_pGraph) { m_pGraph->Release() ; m_pGraph = NULL ; } CBaseVideoPlayer::ReleaseInterfaces() ; // release stanadrd interfaces } // // CDVDPlayer::GetStatusText(): Composes a status message on failure to build DVD graph. // DWORD CDVDPlayer::GetStatusText(AM_DVD_RENDERSTATUS *pStatus, LPTSTR lpszStatusText, DWORD dwMaxText) { DbgLog((LOG_TRACE, 5, TEXT("CDVDPlayer::GetStatusText() entered"))) ; TCHAR achBuffer[1000] ; if (IsBadWritePtr(lpszStatusText, sizeof(*lpszStatusText) * dwMaxText)) { DbgLog((LOG_ERROR, 0, TEXT("GetStatusText(): bad text buffer param"))) ; return 0 ; } int iChars ; LPTSTR lpszBuff = achBuffer ; ZeroMemory(achBuffer, sizeof(TCHAR) * 1000) ; if (pStatus->iNumStreamsFailed > 0) { iChars = wsprintf(lpszBuff, TEXT("* %d out of %d DVD-Video streams failed to render properly\n"), pStatus->iNumStreamsFailed, pStatus->iNumStreams) ; lpszBuff += iChars ; if (pStatus->dwFailedStreamsFlag & AM_DVD_STREAM_VIDEO) { iChars = wsprintf(lpszBuff, TEXT(" - video stream\n")) ; lpszBuff += iChars ; } if (pStatus->dwFailedStreamsFlag & AM_DVD_STREAM_AUDIO) { iChars = wsprintf(lpszBuff, TEXT(" - audio stream\n")) ; lpszBuff += iChars ; } if (pStatus->dwFailedStreamsFlag & AM_DVD_STREAM_SUBPIC) { iChars = wsprintf(lpszBuff, TEXT(" - subpicture stream\n")) ; lpszBuff += iChars ; } } if (FAILED(pStatus->hrVPEStatus)) { lstrcat(lpszBuff, "* ") ; lpszBuff += lstrlen("* ") ; iChars = AMGetErrorText(pStatus->hrVPEStatus, lpszBuff, 200) ; lpszBuff += iChars ; lstrcat(lpszBuff, "\n") ; lpszBuff += lstrlen("\n") ; } if (pStatus->bDvdVolInvalid) { iChars = wsprintf(lpszBuff, TEXT("* Specified DVD-Video volume was invalid\n")) ; lpszBuff += iChars ; } else if (pStatus->bDvdVolUnknown) { iChars = wsprintf(lpszBuff, TEXT("* No valid DVD-Video volume could be located\n")) ; lpszBuff += iChars ; } if (pStatus->bNoLine21In) { iChars = wsprintf(lpszBuff, TEXT("* The video decoder doesn't produce closed caption data\n")) ; lpszBuff += iChars ; } if (pStatus->bNoLine21Out) { iChars = wsprintf(lpszBuff, TEXT("* Decoded closed caption data not rendered properly\n")) ; lpszBuff += iChars ; } DWORD dwLength = (lpszBuff - achBuffer) * sizeof(*lpszBuff) ; dwLength = min(dwLength, dwMaxText) ; lstrcpyn(lpszStatusText, achBuffer, dwLength) ; return dwLength ; } // // CFilePlayer constructor // CFilePlayer::CFilePlayer(void) { DbgLog((LOG_TRACE, 3, TEXT("CFilePlayer c-tor entered"))) ; m_pDDXM = NULL ; } // // CFilePlayer destructor // CFilePlayer::~CFilePlayer(void) { DbgLog((LOG_TRACE, 3, TEXT("CFilePlayer d-tor entered"))) ; ReleaseInterfaces() ; DbgLog((LOG_TRACE, 3, TEXT("CFilePlayer d-tor exiting..."))) ; } // // CFilePlayer::ReleaseInterfaces(): Let go of the interfaces taken in BuildGraph() // void CFilePlayer::ReleaseInterfaces(void) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::ReleaseInterfaces() entered"))) ; if (m_pDDXM) { m_pDDXM->Release() ; m_pDDXM = NULL ; } CBaseVideoPlayer::ReleaseInterfaces() ; } // // CFilePlayer::Initialize(): Creates an empty filter graph object. // BOOL CFilePlayer::Initialize(void) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::Initialize() entered"))) ; // // Just instantiate the Filter Graph object // HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (LPVOID *)&m_pGraph) ; if (FAILED(hr) || NULL == m_pGraph) { DbgLog((LOG_ERROR, 0, TEXT("ERROR: DirectShow is not installed properly (Error 0x%lx)"), hr)) ; return FALSE ; } return TRUE; } // // CFilePlayer::IsOvMConnected(): Private method to detect if the video stream // is passing through the Overlay Mixer (i.e, is it connected?). // BOOL CFilePlayer::IsOvMConnected(IBaseFilter *pOvM) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::IsOvMConnected() entered"))) ; IEnumPins *pEnumPins ; IPin *pPin ; IPin *pPin2 ; ULONG ul ; HRESULT hr ; BOOL bConnected = FALSE ; pOvM->EnumPins(&pEnumPins) ; while (S_OK == pEnumPins->Next(1, &pPin, &ul) && 1 == ul) { hr = pPin->ConnectedTo(&pPin2) ; if (SUCCEEDED(hr) && pPin2) { DbgLog((LOG_TRACE, 3, TEXT("Found pin %s connected to pin %s"), (LPCTSTR)CDisp(pPin), (LPCTSTR)CDisp(pPin2))) ; bConnected = TRUE ; pPin2->Release() ; } pPin->Release() ; } pEnumPins->Release() ; return bConnected ; } // // CFilePlayer::GetVideoRendererInterface(): Private method to get the interface // to the Video Rendererer filter. It also tells us if a file has any video stream. // HRESULT CFilePlayer::GetVideoRendererInterface(IBaseFilter **ppVR) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::IsVideoStreamPresent() entered"))) ; // Get Video Renderer filter pointer from the graph return m_pGraph->FindFilterByName(L"Video Renderer", ppVR) ; } // // CFilePlayer::AddOvMToGraph(): Private method to instantiate OverlayMixer, // add it to the filter graph and set the DDraw parameters specified by the // app.. // HRESULT CFilePlayer::AddOvMToGraph(IBaseFilter **ppOvM, LPDIRECTDRAW pDDObj, LPDIRECTDRAWSURFACE pDDPrimary) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::AddOvMToGraph() entered"))) ; HRESULT hr ; hr = CoCreateInstance(CLSID_OverlayMixer, NULL, CLSCTX_INPROC, IID_IBaseFilter, (LPVOID *)ppOvM) ; if (FAILED(hr) || NULL == *ppOvM) { DbgLog((LOG_ERROR, 0, TEXT("Can't instantiate Overlay Mixer (Error 0x%lx)"), hr)) ; return E_FAIL ; } hr = m_pGraph->AddFilter(*ppOvM, L"Overlay Mixer") ; if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("Can't add Overlay Mixer to the graph (Error 0x%lx)"), hr)) ; (*ppOvM)->Release() ; *ppOvM = NULL ; return E_FAIL ; } // Set the DDraw params now hr = SetDDrawParams(*ppOvM, pDDObj, pDDPrimary) ; if (FAILED(hr)) { m_pGraph->RemoveFilter(*ppOvM) ; // remove from graph (*ppOvM)->Release() ; *ppOvM = NULL ; return hr ; } return S_OK ; } // // CFilePlayer::SetDDrawParams(): Private method to set DDraw objeact and primary // surface on the OverlayMixer via the IDDrawExclModeVideo interface. // HRESULT CFilePlayer::SetDDrawParams(IBaseFilter *pOvM, LPDIRECTDRAW pDDObj, LPDIRECTDRAWSURFACE pDDPrimary) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::SetDDrawParams() entered"))) ; HRESULT hr ; IDDrawExclModeVideo *pDDXM ; hr = pOvM->QueryInterface(IID_IDDrawExclModeVideo, (LPVOID *)&pDDXM) ; if (FAILED(hr) || NULL == pDDXM) { DbgLog((LOG_ERROR, 0, TEXT("Can't get IDDrawExclusiveMode interface from Overlay Mixer (Error 0x%lx)"), hr)) ; return E_FAIL ; } hr = pDDXM->SetDDrawObject(pDDObj) ; if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("SetDDrawObject(0x%lx) failed (Error 0x%lx)"), pDDObj, hr)) ; pDDXM->Release() ; // release interface return E_FAIL ; } hr = pDDXM->SetDDrawSurface(pDDPrimary) ; if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("SetDDrawSurface(0x%lx) failed (Error 0x%lx)"), pDDPrimary, hr)) ; pDDXM->SetDDrawObject(NULL) ; // get back to original DDraw object pDDXM->Release() ; // release interface return E_FAIL ; } pDDXM->Release() ; // release interface return S_OK ; } // // CFilePlayer::PutVideoThroughOvM(): Private method to connect the decoded video // stream through the OverlayMixer. // HRESULT CFilePlayer::PutVideoThroughOvM(IBaseFilter *pOvM, IBaseFilter *pVR) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::PutVideoThroughOvM() entered"))) ; IEnumPins *pEnumPins ; IPin *pPinVR ; IPin *pPinConnectedTo ; IPin *pPinOvM ; ULONG ul ; PIN_DIRECTION pd ; HRESULT hr ; // Get the video renderer's (only) in pin hr = pVR->EnumPins(&pEnumPins) ; ASSERT(SUCCEEDED(hr)) ; hr = pEnumPins->Next(1, &pPinVR, &ul) ; ASSERT(S_OK == hr && 1 == ul) ; pPinVR->QueryDirection(&pd) ; ASSERT(PINDIR_INPUT == pd) ; pEnumPins->Release() ; // Disconnect VR from upstream filter and put OvMixer in between them hr = pPinVR->ConnectedTo(&pPinConnectedTo) ; ASSERT(SUCCEEDED(hr) && pPinConnectedTo) ; hr = m_pGraph->Disconnect(pPinVR) ; ASSERT(SUCCEEDED(hr)) ; hr = m_pGraph->Disconnect(pPinConnectedTo) ; ASSERT(SUCCEEDED(hr)) ; // Now connect the upstream filter's out pin to OvM's in pin // (and remove Video Renderer from the graph). hr = pOvM->EnumPins(&pEnumPins) ; ASSERT(SUCCEEDED(hr)) ; int iInConns = 0 ; while (iInConns == 0 && // input pin not connected yet S_OK == pEnumPins->Next(1, &pPinOvM, &ul) && 1 == ul) // pin left to try { pPinOvM->QueryDirection(&pd) ; if (PINDIR_INPUT == pd) // OvM's in pin <- Upstream filter's pin { if (0 == iInConns) // We want to connect only one input pin { hr = m_pGraph->Connect(pPinConnectedTo, pPinOvM) ; // , NULL) ; // direct?? ASSERT(SUCCEEDED(hr)) ; if (FAILED(hr)) DbgLog((LOG_ERROR, 0, TEXT("ERROR: m_pGraph->Connect() failed (Error 0x%lx)"), hr)) ; else { DbgLog((LOG_TRACE, 5, TEXT("Pin %s connected to pin %s"), (LPCTSTR) CDisp(pPinConnectedTo), (LPCTSTR) CDisp(pPinOvM))) ; iInConns++ ; } } // else ignore } else DbgLog((LOG_ERROR, 1, TEXT("OVMixer still has an out pin!!!"))) ; pPinOvM->Release() ; } pEnumPins->Release() ; pPinConnectedTo->Release() ; // done with upstream pin pPinVR->Release() ; // done with VR pin m_pGraph->RemoveFilter(pVR) ; // by force remove the VR from graph // Just to check... if (0 == iInConns) // input pin not connected!! { DbgLog((LOG_ERROR, 0, TEXT("WARNING: Couldn't connect any out pin to OvMixer's 1st in pin"))) ; return E_FAIL ; // failure } return S_OK ; // success!! } // // CFilePlayer::BuildGraph(): Builds a AVI/MPEG/.. playback graph rendering via // OverlayMixer which uses app's given DDraw params. // HRESULT CFilePlayer::BuildGraph(HWND hWndApp, LPDIRECTDRAW pDDObj, LPDIRECTDRAWSURFACE pDDPrimary) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::BuildGraph() entered"))) ; HRESULT hr ; IBaseFilter *pOvM ; WCHAR achwFileName[MAX_PATH] ; LPWSTR lpszwFileName = NULL ; IBaseFilter *pVR ; // First release any existing interface pointer(s) ReleaseInterfaces() ; SetColorKey(253) ; // default magenta for 8bpp // Check if a file name has been specified; if so, use that if (lstrlen(GetFileName()) > 0) // if something was specified before { #ifdef UNICODE lstrcpy(achwFileName, GetFileName()) ; #else MultiByteToWideChar(CP_ACP, 0, GetFileName(), -1, achwFileName, MAX_PATH) ; #endif // UNICODE lpszwFileName = achwFileName ; } else // no file specified, but we should have detected it before!!! return E_FAIL ; // // Instantiate Overlay Mixer, add it to the graph and set DDraw params // hr = AddOvMToGraph(&pOvM, pDDObj, pDDPrimary) ; if (FAILED(hr)) { return E_FAIL ; } // // First render the graph for the selected file // hr = m_pGraph->RenderFile(lpszwFileName, NULL) ; if (S_OK != hr) { DbgLog((LOG_ERROR, 0, TEXT("Rendering the given file didn't succeed completely (Error 0x%lx)"), hr)) ; m_pGraph->RemoveFilter(pOvM) ; // remove from graph pOvM->Release() ; // release filter return E_FAIL ; } // // Because there are some AVI files which on some machines decide to rather go // through the Color Space Converter filter, just making sure that the OverlayMixer // is actually being used. Otherwise we have to do some more (bull)work. // if (! IsOvMConnected(pOvM) ) { DbgLog((LOG_TRACE, 1, TEXT("OverlayMixer is not used in the graph. Try again..."))) ; // // Check that the specified file has a video stream. Otherwise OverlayMixer // will never be used and DDraw exclusive mode playback doesn't make any sense. // if (FAILED(GetVideoRendererInterface(&pVR))) { DbgLog((LOG_TRACE, 1, TEXT("Specified file doesn't have any video stream. Aborting graph building."))) ; m_pGraph->RemoveFilter(pOvM) ; // remove from graph pOvM->Release() ; // release filter return E_FAIL ; } // // Put the video stream to go through the OverlayMixer. // hr = PutVideoThroughOvM(pOvM, pVR) ; if (FAILED(hr)) { DbgLog((LOG_TRACE, 1, TEXT("Couldn't put video through the OverlayMixer."))) ; m_pGraph->RemoveFilter(pOvM) ; // remove OvMixer from graph pOvM->Release() ; // release OvMixer filter pVR->Release() ; // release VR interface (before giving up) return E_FAIL ; } pVR->Release() ; // done with VR interface } // // We are successful in building the graph. Now the rest... // GetInterfaces(hWndApp) ; // Get IDDrawExclModeVideo interface of the OvMixer and store it hr = pOvM->QueryInterface(IID_IDDrawExclModeVideo, (LPVOID *)&m_pDDXM) ; ASSERT(SUCCEEDED(hr)) ; // Get the color key to be used and store it hr = GetColorKeyInternal(pOvM) ; ASSERT(SUCCEEDED(hr)) ; pOvM->Release() ; // done with it return S_OK ; } // // CFilePlayer::GetInterfaces(): Gets some interfaces useful for DVD title playback // // NOTE: Don't really need this one. We could have as well used the base classes'. // HRESULT CFilePlayer::GetInterfaces(HWND hWndApp) { return CBaseVideoPlayer::GetInterfaces(hWndApp) ; // get the standard interfaces } // // CFilePlayer::GetColorKeyInternal(): Private method to query the color key // value from teh first input pin of the OverlayMixer. // HRESULT CFilePlayer::GetColorKeyInternal(IBaseFilter *pOvM) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::GetColorKeyInternal() entered"))) ; if (NULL == pOvM) return E_INVALIDARG ; IEnumPins *pEnumPins ; IPin *pPin ; ULONG ul ; PIN_DIRECTION pd ; DWORD dwColorKey ; IMixerPinConfig *pMPC ; HRESULT hr = pOvM->EnumPins(&pEnumPins) ; ASSERT(pEnumPins) ; while (S_OK == pEnumPins->Next(1, &pPin, &ul) && 1 == ul) // try all pins { pPin->QueryDirection(&pd) ; if (PINDIR_INPUT == pd) // only the 1st in pin { hr = pPin->QueryInterface(IID_IMixerPinConfig, (LPVOID *) &pMPC) ; ASSERT(SUCCEEDED(hr) && pMPC) ; hr = pMPC->GetColorKey(NULL, &dwColorKey) ; // just get the physical color SetColorKey(dwColorKey) ; // Set mode to stretch - that way we don't fight the overlay // mixer about the exact way to fix the aspect ratio pMPC->SetAspectRatioMode(AM_ARMODE_STRETCHED); ASSERT(SUCCEEDED(hr)) ; pMPC->Release() ; pPin->Release() ; // exiting early; release pin break ; // we are done } pPin->Release() ; } pEnumPins->Release() ; // done with pin enum return S_OK ; } // // CFilePlayer::ClearGraph(): Releases the filter graph built by BuildGraph() // and the interfaces taken for playback. // HRESULT CFilePlayer::ClearGraph(void) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::ClearGraph() entered"))) ; ReleaseInterfaces() ; // let go of the other interfaces if (m_pGraph) { m_pGraph->Release() ; // let the graph go (we'll create a fresh one next time) m_pGraph = NULL ; } return S_OK ; } // // CFilePlayer::SetVideoPosition(): Sets the source and dest rects for the video to // be shown via IDDrawExclModeVideo interface of OverlayMixer. // HRESULT CFilePlayer::SetVideoPosition(DWORD dwLeft, DWORD dwTop, DWORD dwWidth, DWORD dwHeight) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::SetVideoPosition() entered"))) ; RECT rectDest ; SetRect(&rectDest, dwLeft, dwTop, dwLeft + dwWidth, dwTop + dwHeight) ; // NULL or (0, 0, 10000, 10000) for full video return m_pDDXM->SetDrawParameters(NULL, &rectDest) ; } // // CFilePlayer::GetNativeVideoData(): Returns the actual video size to the app. // HRESULT CFilePlayer::GetNativeVideoData(DWORD *pdwWidth, DWORD *pdwHeight, DWORD *pdwARX, DWORD *pdwARY) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::GetNativeVideoData() entered"))) ; return m_pDDXM->GetNativeVideoProps(pdwWidth, pdwHeight, pdwARX, pdwARY) ; } // // CFilePlayer::SetOverlayCallback(): Specify the pointer to the overlay notification // callback object which the OverlayMixer will call during playback. // HRESULT CFilePlayer::SetOverlayCallback(IDDrawExclModeVideoCallback *pCallback) { DbgLog((LOG_TRACE, 5, TEXT("CFilePlayer::SetOverlayCallback() entered"))) ; HRESULT hr ; hr = m_pDDXM->SetCallbackInterface(pCallback, 0) ; if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("ERROR: IDDrawExclModeVideo::SetCallbackInterface() failed (Error 0x%lx)"), hr)) ; } return hr ; }