MEDIA.C

//==========================================================================; 
//
// 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.
//
//--------------------------------------------------------------------------;

#include "stdwin.h"
#include <evcode.h>
#include "cplay.h"
#include "file.h"
#include "media.h"
#include "toolbar.h"
#include "resource.h"

// Current multimedia variables
static Media media;


//
// CanPlay
//
// Return true if we can go to a playing state from our current state
//
BOOL CanPlay()
{
return (media.state == Stopped || media.state == Paused);
}


//
// CanStop
//
// Return true if we can go to a stopped state from our current state
//
BOOL CanStop()
{
return (media.state == Playing || media.state == Paused);
}


//
// CanPause
//
// Return true if we can go to a paused state from our current state
//
BOOL CanPause()
{
return (media.state == Playing || media.state == Stopped);
}


//
// IsInitialized
//
// Return true if we have loaded and initialized a multimedia file
//
BOOL IsInitialized()
{
return (media.state != Uninitialized);
}


//
// ChangeStateTo
//
void ChangeStateTo( State newState )
{
media.state = newState;

// update the toolbar
UpdateToolbar();
}


//
// InitMedia
//
// Initialization
//
BOOL InitMedia()
{
ChangeStateTo( Uninitialized );

media.hGraphNotifyEvent = NULL;
media.pGraph = NULL;

return TRUE;
}


//
// CreateFilterGraph
//
BOOL CreateFilterGraph()
{
IMediaEvent *pME;
HRESULT hr;

ASSERT(media.pGraph == NULL);

hr = CoCreateInstance(&CLSID_FilterGraph, // CLSID of object
NULL, // Outer unknown
CLSCTX_INPROC_SERVER, // Type of server
&IID_IGraphBuilder, // Interface wanted
(void **) &media.pGraph); // Returned object
if (FAILED(hr)){
media.pGraph = NULL;
return FALSE;
}

// We use this to find out events sent by the filtergraph

hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaEvent, (void **) &pME);
if (FAILED(hr)) {
DeleteContents();
return FALSE;
}

hr = pME->lpVtbl->GetEventHandle(pME, (OAEVENT*) &media.hGraphNotifyEvent);
pME->lpVtbl->Release( pME );

if (FAILED(hr)) {
DeleteContents();
return FALSE;
}

return TRUE;

} // CreateFilterGraph


// Destruction
//
// DeleteContents
//
void DeleteContents()
{
if (media.pGraph != NULL) {
media.pGraph->lpVtbl->Release( media.pGraph );
media.pGraph = NULL;
}

// this event is owned by the filter graph and is thus invalid
media.hGraphNotifyEvent = NULL;

ChangeStateTo( Uninitialized );
}


//
// RenderFile
//
BOOL RenderFile( LPSTR szFileName )
{
HRESULT hr;
WCHAR wPath[MAX_PATH];

DeleteContents();

if ( !CreateFilterGraph() ) {
PlayerMessageBox( IDS_CANT_INIT_QUARTZ );
return FALSE;
}

MultiByteToWideChar( CP_ACP, 0, szFileName, -1, wPath, MAX_PATH );

SetCursor( LoadCursor( NULL, IDC_WAIT ) );
hr = media.pGraph->lpVtbl->RenderFile(media.pGraph, wPath, NULL);
SetCursor( LoadCursor( NULL, IDC_ARROW ) );

if (FAILED( hr )) {
PlayerMessageBox( IDS_CANT_RENDER_FILE );
return FALSE;
}
return TRUE;

} // RenderFile


//
// SetTitle
//
// Update the title of the video renderer to "Player - szFile"
//
void SetTitle( HWND hwnd, char *szFile )
{
char szNewTitle[ _MAX_FNAME + _MAX_EXT + 20 ];

strcpy( szNewTitle, APP_NAME );
strcat( szNewTitle, " - " );
strcat( szNewTitle, szFile );

// Update the window's title
SetWindowText( hwnd, szNewTitle );

} // SetTitle


//
// OpenMediaFile
//
// File..Open has been selected
//
void OpenMediaFile( HWND hwnd, LPSTR szFile )
{
static char szFileName[ _MAX_PATH ];
static char szTitleName[ _MAX_FNAME + _MAX_EXT ];

if( szFile != NULL && RenderFile( szFile ))
{
LPSTR szTitle;

// Work out the full path name and the file name from the
// specified file
GetFullPathName( szFile, _MAX_PATH, szFileName, &szTitle );
strncpy( szTitleName, szTitle, _MAX_FNAME + _MAX_EXT );
szTitleName[ _MAX_FNAME + _MAX_EXT -1 ] = '\0';

// Set the main window title and update the state
SetTitle( hwnd, szTitleName );
ChangeStateTo( Stopped );

} else if( DoFileOpenDialog( hwnd, szFileName, szTitleName )
&& RenderFile( szFileName ) ){

// Set the main window title and update the state
SetTitle( hwnd, szTitleName );
ChangeStateTo( Stopped );
}

} // OpenMediaFile


//
// OnMediaPlay
//
// There are two possible UI strategies for an application: you either play
// from the start each time you stop, or you play from the current position,
// in which case you have to explicitly rewind at the end. If you want the
// play from current and rewind at end, enable this code, if you want the
// other option, then enable FROM_START in both OnMediaStop and OnAbortStop.

#undef REWIND
#define FROM_START

void OnMediaPlay()
{
if( CanPlay() ){
HRESULT hr;
IMediaControl *pMC;

// Obtain the interface to our filter graph
hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaControl, (void **) &pMC);

if( SUCCEEDED(hr) ){
#ifdef REWIND
IMediaPosition * pMP;
hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
&IID_IMediaPosition,
(void**) &pMP);
if (SUCCEEDED(hr)) {
// start from last position, but rewind if near the end
REFTIME tCurrent, tLength;
hr = pMP->lpVtbl->get_Duration(pMP, &tLength);
if (SUCCEEDED(hr)) {
hr = pMP->lpVtbl->get_CurrentPosition(pMP, &tCurrent);
if (SUCCEEDED(hr)) {
// within 1sec of end? (or past end?)
if ((tLength - tCurrent) < 1) {
pMP->lpVtbl->put_CurrentPosition(pMP, 0);
}
}
}
pMP->lpVtbl->Release(pMP);
}
#endif
// Ask the filter graph to play and release the interface
hr = pMC->lpVtbl->Run( pMC );
pMC->lpVtbl->Release( pMC );

if( SUCCEEDED(hr) ){
ChangeStateTo( Playing );
return;
}
}

// Inform the user that an error occurred
PlayerMessageBox( IDS_CANT_PLAY );

}

} // OnMediaPlay


//
// OnMediaPause
//
void OnMediaPause()
{
if( CanPause() ){
HRESULT hr;
IMediaControl *pMC;

// Obtain the interface to our filter graph
hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaControl, (void **) &pMC);

if( SUCCEEDED(hr) ){
// Ask the filter graph to pause and release the interface
hr = pMC->lpVtbl->Pause( pMC );
pMC->lpVtbl->Release( pMC );

if( SUCCEEDED(hr) ){
ChangeStateTo( Paused );
return;
}
}

// Inform the user that an error occurred
PlayerMessageBox( IDS_CANT_PAUSE );
}

} // OnMediaPause


//
// OnMediaAbortStop
//
// Called when we get an EC_USER_ABORT or EC_ERROR_ABORT event
//
void OnMediaAbortStop(void)
{
if(CanStop())
{
HRESULT hr;
IMediaControl *pMC;

// Obtain the interface to our filter graph
hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaControl, (void **) &pMC);

if( SUCCEEDED(hr) ){
#ifdef FROM_START
IMediaPosition * pMP;
#endif
// Ask the filter graph to stop and release the interface
hr = pMC->lpVtbl->Stop( pMC );
pMC->lpVtbl->Release( pMC );

#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).
hr = media.pGraph->lpVtbl->QueryInterface(
media.pGraph,
&IID_IMediaPosition,
(void**) &pMP);
if (SUCCEEDED(hr)) {
pMP->lpVtbl->put_CurrentPosition(pMP, 0);
pMP->lpVtbl->Release(pMP);
}

// no visible rewind or we will re-show the window!

#endif

if( SUCCEEDED(hr) ){
ChangeStateTo( Stopped );
return;
}
}
// Inform the user that an error occurred
PlayerMessageBox( IDS_CANT_STOP );
}

} // OnMediaAbortStop


//
// OnMediaStop
//
// 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 OnMediaStop()
{
if( CanStop() ){
HRESULT hr;
IMediaControl *pMC;

// Obtain the interface to our filter graph
hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaControl, (void **) &pMC);
if( SUCCEEDED(hr) ){

#ifdef FROM_START
IMediaPosition * pMP;
OAFilterState state;

// Ask the filter graph to pause
hr = pMC->lpVtbl->Pause( pMC );

// 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 = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
&IID_IMediaPosition,
(void**) &pMP);
if (SUCCEEDED(hr)) {
pMP->lpVtbl->put_CurrentPosition(pMP, 0);
pMP->lpVtbl->Release(pMP);
}

// wait for pause to complete
pMC->lpVtbl->GetState(pMC, INFINITE, &state);
#endif

// now really do the stop
pMC->lpVtbl->Stop( pMC );
pMC->lpVtbl->Release( pMC );
ChangeStateTo( Stopped );
return;
}

// Inform the user that an error occurred
PlayerMessageBox( IDS_CANT_STOP );
}

} // OnMediaStop


//
// GetGraphEventHandle
//
// We use this to check for graph events
//
HANDLE GetGraphEventHandle()
{
return media.hGraphNotifyEvent;

} // GetGraphEventHandle


//
// OnGraphNotify
//
// If the event handle is valid, then ask the graph if
// anything has happened (eg the graph has stopped...)
//
void OnGraphNotify()
{
IMediaEvent *pME;
long lEventCode, lParam1, lParam2;

ASSERT( media.hGraphNotifyEvent != NULL );

if( SUCCEEDED(media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaEvent, (void **) &pME))){
if( SUCCEEDED(pME->lpVtbl->GetEvent(pME, &lEventCode, &lParam1, &lParam2, 0))) {
if (lEventCode == EC_COMPLETE) {
OnMediaStop();
} else if ((lEventCode == EC_USERABORT) ||
(lEventCode == EC_ERRORABORT)) {
OnMediaAbortStop();
}
}
pME->lpVtbl->Release( pME );
}
}