MFCDOC.CPP

//==========================================================================; 
//
// 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 - 1997 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
//
// playdoc.cpp : implementation of the CPlayerDoc class
//

#include "stdafx.h"
#include <reftime.h>
#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() ){
HRESULThr;
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() ){
HRESULThr;
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() ){
HRESULThr;
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() ){
HRESULThr;
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)
{
WCHARwPath[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))) {

// 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 );

}