//==========================================================================; // // 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) 1992 - 1998 Microsoft Corporation. All Rights Reserved. // //--------------------------------------------------------------------------; // // playdoc.cpp : implementation of the CPlayerDoc class // #include "stdafx.h" #include #include "mfcplay.h" #include "mfcdoc.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CPlayerDoc IMPLEMENT_DYNCREATE(CPlayerDoc, CDocument) BEGIN_MESSAGE_MAP(CPlayerDoc, CDocument) //{{AFX_MSG_MAP(CPlayerDoc) ON_UPDATE_COMMAND_UI(ID_MEDIA_PLAY, OnUpdateMediaPlay) ON_UPDATE_COMMAND_UI(ID_MEDIA_PAUSE, OnUpdateMediaPause) ON_UPDATE_COMMAND_UI(ID_MEDIA_STOP, OnUpdateMediaStop) ON_COMMAND(ID_MEDIA_PLAY, OnMediaPlay) ON_COMMAND(ID_MEDIA_PAUSE, OnMediaPause) ON_COMMAND(ID_MEDIA_STOP, OnMediaStop) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CPlayerDoc construction/destruction CPlayerDoc::CPlayerDoc() { // TODO: add one-time construction code here m_pGraph = NULL; m_State = Uninitialized; m_hGraphNotifyEvent = NULL; ((CPlayerApp *) AfxGetApp())->OnDocumentCreated( this ); } BOOL CPlayerDoc::CreateFilterGraph(void) { HRESULT hr; // return code ASSERT(m_pGraph == NULL); hr = CoCreateInstance(CLSID_FilterGraph, // get this documents graph object NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **) &m_pGraph); if (FAILED(hr)){ m_pGraph = NULL; return FALSE; } // get hold of the event notification handle so we can wait for // completion IMediaEvent *pME; hr = m_pGraph->QueryInterface(IID_IMediaEvent, (void **) &pME); if (FAILED(hr)) { DeleteContents(); return FALSE; } hr = pME->GetEventHandle((OAEVENT*) &m_hGraphNotifyEvent); pME->Release(); if (FAILED(hr)) { DeleteContents(); return FALSE; } return TRUE; } void CPlayerDoc::DeleteContents( void ){ if (m_pGraph != NULL) { m_pGraph->Release(); m_pGraph = NULL; } // this event is owned by the filtergraph and thus is no longer valid m_hGraphNotifyEvent = NULL; m_State = Uninitialized; } CPlayerDoc::~CPlayerDoc() { DeleteContents(); ((CPlayerApp *) AfxGetApp())->OnDocumentDestroyed( this ); } ///////////////////////////////////////////////////////////////////////////// // CPlayerDoc diagnostics #ifdef _DEBUG void CPlayerDoc::AssertValid() const { CDocument::AssertValid(); } void CPlayerDoc::Dump(CDumpContext& dc) const { CDocument::Dump(dc); } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CPlayerDoc commands void CPlayerDoc::OnUpdateMediaPlay(CCmdUI* pCmdUI) { pCmdUI->Enable( CanPlay() ); } void CPlayerDoc::OnUpdateMediaPause(CCmdUI* pCmdUI) { pCmdUI->Enable( CanPause() ); } void CPlayerDoc::OnUpdateMediaStop(CCmdUI* pCmdUI) { pCmdUI->Enable( CanStop() ); } void CPlayerDoc::OnMediaPlay() { if( CanPlay() ){ HRESULT hr; IMediaControl *pMC; // Obtain the interface to our filter graph hr = m_pGraph->QueryInterface(IID_IMediaControl, (void **) &pMC); if( SUCCEEDED(hr) ){ // Ask the filter graph to play and release the interface // default behaviour is to carry on from where we stopped last // time. // // if you want it to do this, but rewind at the end then // define REWIND. // Otherwise you probably want to always start from the // beginning -> define FROM_START (in OnMediaStop) #undef REWIND #define FROM_START #ifdef REWIND IMediaPosition * pMP; hr = m_pGraph->QueryInterface(IID_IMediaPosition, (void**) &pMP); if (SUCCEEDED(hr)) { // start from last position, but rewind if near the // end REFTIME tCurrent, tLength; hr = pMP->get_Duration(&tLength); if (SUCCEEDED(hr)) { hr = pMP->get_CurrentPosition(&tCurrent); if (SUCCEEDED(hr)) { // within 1sec of end? (or past end?) if ((tLength - tCurrent) < 1) { pMP->put_CurrentPosition(0); } } } pMP->Release(); } #endif hr = pMC->Run(); pMC->Release(); if( SUCCEEDED(hr) ){ m_State=Playing; return; } } // Inform the user that an error occurred AfxMessageBox(IDS_CANT_PLAY); } } void CPlayerDoc::OnMediaPause() { if( CanPause() ){ HRESULT hr; IMediaControl *pMC; // Obtain the interface to our filter graph hr = m_pGraph->QueryInterface(IID_IMediaControl, (void **) &pMC); if( SUCCEEDED(hr) ){ // Ask the filter graph to pause and release the interface hr = pMC->Pause(); pMC->Release(); if( SUCCEEDED(hr) ){ m_State = Paused; return; } } // Inform the user that an error occurred AfxMessageBox(IDS_CANT_PAUSE); } } // stop the graph without rewinding void CPlayerDoc::OnAbortStop() { if( CanStop() ){ HRESULT hr; IMediaControl *pMC; // Obtain the interface to our filter graph hr = m_pGraph->QueryInterface(IID_IMediaControl, (void **) &pMC); if( SUCCEEDED(hr) ){ // Ask the filter graph to stop and release the interface hr = pMC->Stop(); #ifdef FROM_START // if we want always to play from the beginning // then we should seek back to the start here // (on app or user stop request, and also after EC_COMPLETE). IMediaPosition * pMP; hr = m_pGraph->QueryInterface(IID_IMediaPosition, (void**) &pMP); if (SUCCEEDED(hr)) { pMP->put_CurrentPosition(0); pMP->Release(); } // no visible rewind or we will re-show the window! #endif pMC->Release(); if( SUCCEEDED(hr) ){ m_State = Stopped; return; } } // Inform the user that an error occurred AfxMessageBox(IDS_CANT_STOP); } } // There are two different ways to stop a graph. We can stop and then when we // are later paused or run continue from the same position. Alternatively the // graph can be set back to the start of the media when it is stopped to have // a more CDPLAYER style interface. These are both offered here conditionally // compiled using the FROM_START definition. The main difference is that in // the latter case we put the current position to zero while we change states void CPlayerDoc::OnMediaStop() { if( CanStop() ){ HRESULT hr; IMediaControl *pMC; // Obtain the interface to our filter graph hr = m_pGraph->QueryInterface(IID_IMediaControl, (void **) &pMC); if( SUCCEEDED(hr) ){ #ifdef FROM_START IMediaPosition * pMP; OAFilterState state; // Ask the filter graph to pause hr = pMC->Pause(); // if we want always to play from the beginning // then we should seek back to the start here // (on app or user stop request, and also after EC_COMPLETE). hr = m_pGraph->QueryInterface(IID_IMediaPosition, (void**) &pMP); if (SUCCEEDED(hr)) { pMP->put_CurrentPosition(0); pMP->Release(); } // wait for pause to complete pMC->GetState(INFINITE, &state); #endif // now really do the stop pMC->Stop(); pMC->Release(); m_State = Stopped; return; } // Inform the user that an error occurred AfxMessageBox(IDS_CANT_STOP); } } BOOL CPlayerDoc::OnOpenDocument(LPCTSTR lpszPathName) { WCHAR wPath[MAX_PATH]; DeleteContents(); if ( !CreateFilterGraph() ) { AfxMessageBox(IDS_CANT_INIT_QUARTZ); return FALSE; } MultiByteToWideChar( CP_ACP, 0, lpszPathName, -1, wPath, MAX_PATH ); if (FAILED( m_pGraph->RenderFile(wPath, NULL) )) { AfxMessageBox(IDS_CANT_RENDER_FILE); return FALSE; } m_State = Stopped; return TRUE; } // // If the event handle is valid, ask the graph // if anything has happened. eg the graph has stopped... void CPlayerDoc::OnGraphNotify(void) { IMediaEvent *pME; long lEventCode, lParam1, lParam2; ASSERT( m_hGraphNotifyEvent != NULL ); if( SUCCEEDED(m_pGraph->QueryInterface(IID_IMediaEvent, (void **) &pME))) { if( SUCCEEDED(pME->GetEvent(&lEventCode, &lParam1, &lParam2, 0))) { HRESULT hrTmp = pME->FreeEventParams(lEventCode, lParam1, lParam2); ASSERT(SUCCEEDED(hrTmp)); // if this is a normal stop, then we do a rewind as // we would if the user pressed stop. For an abort, // we don't touch this. if (lEventCode == EC_COMPLETE) { OnMediaStop(); } else if ((lEventCode == EC_ERRORABORT) || (lEventCode == EC_USERABORT)) { // put the graph into stop mode but don't mess with it. OnAbortStop(); } } pME->Release(); } // The toolbar will only be updated after a message has been received, // so send a dummy message PostMessage( AfxGetMainWnd()->m_hWnd, WM_WAKEUP, 0, 0 ); }