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