//==========================================================================; // // 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. // //--------------------------------------------------------------------------; #include "stdwin.h" #include #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 ); } }