//==========================================================================; // // 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 #include "Ext_Enum.h" class CAMovie : public IAMovie, public CUnknown { private: IGraphBuilder * pGB; IMediaControl * pMC; IMediaEvent * pME; IMediaPosition * pMP; protected: ~CAMovie(); CAMovie(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr); // Disconnect all pins. If a filter has no pins, remove it completely // This is to part-destroy a filter graph, leaving only those filters // that may be usefult for rendering a file or pin after this function // has been called. HRESULT DisconnectAllAndRemoveIsolates(); // For all filters in graph, if the filter has pins, and all are unconnected, // then remove the filter from the graph. HRESULT RemoveAllUnconnected(); public: DECLARE_IUNKNOWN static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr); STDMETHODIMP NonDelegatingQueryInterface(REFIID iid, void ** ppv); // IFilterGraph // Add a filter to the graph and name it with *pName. // If the name is not unique, The request will fail. // The Filter graph will call the JoinFilterGraph // member function of the filter to inform it. // This must be called before attempting Connect, ConnectDirect or Render // for pins of the filter. STDMETHODIMP AddFilter ( IBaseFilter * pFilter, LPCWSTR pName ); // Remove a filter from the graph. The filter graph implementation // will inform the filter that it is being removed. STDMETHODIMP RemoveFilter ( IBaseFilter * pFilter ); // Set *ppEnum to be an enumerator for all filters in the graph. STDMETHODIMP EnumFilters ( IEnumFilters **ppEnum ); // Set *ppFilter to be the filter which was added with the name *pName // Will fail and set *ppFilter to NULL if the name is not in this graph. STDMETHODIMP FindFilterByName ( LPCWSTR pName, IBaseFilter ** ppFilter ); //========================================================================== // Low level connection functions //========================================================================== // Connect these two pins directly (i.e. without intervening filters) // the media type is optional, and may be partially specified (that is // the subtype and/or format type may be GUID_NULL). See IPin::Connect // for details of the media type parameter. STDMETHODIMP ConnectDirect ( IPin * ppinOut, // the output pin IPin * ppinIn, // the input pin const AM_MEDIA_TYPE* pmt // optional mediatype ); // On a separate thread (which will not hold any relevant locks) // Break the connection that this pin has and reconnect it to the // same other pin. STDMETHODIMP Reconnect ( IPin * ppin // the pin to disconnect and reconnect ); // Disconnect this pin, if connected. Successful no-op if not connected. STDMETHODIMP Disconnect ( IPin * ppin ); //========================================================================== // intelligent connectivity - now in IGraphBuilder, axextend.idl //========================================================================== //========================================================================== // Whole graph functions //========================================================================== // Once a graph is built, it can behave as a (composite) filter. // To control this filter, QueryInterface for IMediaFilter. // The filtergraph will by default ensure that the graph has a sync source // when it is made to Run. SetSyncSource(NULL) will prevent that and allow // all the filters to run unsynchronised until further notice. // SetDefaultSyncSource will set the default sync source (the same as would // have been set by default on the first call to Run). STDMETHODIMP SetDefaultSyncSource(void); // IGraphBuilder // Connect these two pins directly or indirectly, using transform filters // if necessary. STDMETHODIMP Connect ( IPin * ppinOut, // the output pin IPin * ppinIn // the input pin ); // Connect this output pin directly or indirectly, using transform filters // if necessary to something that will render it. STDMETHODIMP Render ( IPin * ppinOut // the output pin ); // This pair of methods are a cross between the IGraphBuilder ones and the IMediaControl ones, // unfortunate perhaps, but they do make the nicest C / C++ interfaces . // adds and connects filters needed to play the specified file STDMETHODIMP RenderFile( LPCWSTR strFilename ); // adds to the graph the source filter that can read this file, // and returns an IFilterInfo object for it STDMETHODIMP AddSourceFilter( LPCWSTR strFilename, IBaseFilter ** ppUnk); // IMediaControl methods STDMETHODIMP Run(); STDMETHODIMP Pause(); STDMETHODIMP Stop(); //returns the state. same semantics as IMediaFilter::GetState STDMETHODIMP GetState( LONG msTimeout, FILTER_STATE* pfs); // IMediaEvent // get back the event handle. This is manual-reset // (don't - it's reset by the event mechanism) and remains set // when events are queued, and reset when the queue is empty. STDMETHODIMP GetEventHandle( HEVENT * hEvent); // remove the next event notification from the head of the queue and // return it. Waits up to msTimeout millisecs if there are no events. // if a timeout occurs without any events, this method will return // E_ABORT, and the value of the event code and other parameters // is undefined. STDMETHODIMP GetEvent( long * lEventCode, long * lParam1, long * lParam2, long msTimeout ); // Free any resources associated with the parameters to an event. // Event parameters may be LONGs, IUnknown* or BSTR. No action // is taken with LONGs. IUnknown are passed addrefed and need a // Release call. BSTR are allocated by the task allocator and will be // freed by calling the task allocator. STDMETHODIMP FreeEventParams( long lEvCode, long lParam1, long lParam2); // Calls GetEvent repeatedly discarding events until it finds a // completion event (EC_COMPLETE, EC_ERRORABORT, or EC_USERABORT). // The completion event is removed from the queue and returned // in pEvCode. Note that the object is still in running mode until // a Pause or Stop call is made. // If the timeout occurs, *pEvCode will be 0 and E_ABORT will be // returned. STDMETHODIMP WaitForCompletion( long msTimeout, long * pEvCode); // cancels any system handling of the specified event code // and ensures that the events are passed straight to the application // (via GetEvent) and not handled. A good example of this is // EC_REPAINT: default handling for this ensures the painting of the // window and does not get posted to the app. STDMETHODIMP CancelDefaultHandling( long lEvCode); // restore the normal system default handling that may have been // cancelled by CancelDefaultHandling(). STDMETHODIMP RestoreDefaultHandling( long lEvCode); // IMediaPosition // properties STDMETHODIMP get_Duration( REFTIME* plength); STDMETHODIMP put_CurrentPosition( REFTIME llTime); STDMETHODIMP get_CurrentPosition( REFTIME* pllTime); STDMETHODIMP get_StopTime( REFTIME* pllTime); STDMETHODIMP put_StopTime( REFTIME llTime); STDMETHODIMP get_PrerollTime( REFTIME* pllTime); STDMETHODIMP put_PrerollTime( REFTIME llTime); STDMETHODIMP put_Rate( double dRate); STDMETHODIMP get_Rate( double * pdRate); /* New methods */ // Remove all filters STDMETHODIMP RemoveAllFilters(); // If the filter graph can be Run to completion, run it and wait for it to complete STDMETHODIMP Play(); // As above, but RenderNewFile first STDMETHODIMP PlayFile( LPCWSTR strFilename ); // Render all unconnected output pins STDMETHODIMP RenderAll(); // Render supplied files "as if" the filter graoh were first emptied STDMETHODIMP RenderNewFile( LPCWSTR strFilename ); STDMETHODIMP EnumFiltersByInterface( REFIID iid, IEnumFilters ** pEnumFilters ); // Enum pins over the entire graph STDMETHODIMP EnumPins( IEnumPins ** ppEnum ); STDMETHODIMP EnumPinsIn( IEnumPins ** ppEnum ); STDMETHODIMP EnumPinsOut( IEnumPins ** ppEnum ); }; CFactoryTemplate g_Templates[]= { {L"ActiveMovie Sample PID", &CLSID_AMovie, CAMovie::CreateInstance}, }; int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]); CAMovie::~CAMovie() { /* nothing to do */ } CAMovie::CAMovie(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr) : CUnknown( pName, pUnk ) , pGB(0), pMC(0), pME(0), pMP(0) { if (!pUnk) *phr = VFW_E_NEED_OWNER; else if SUCCEEDED(*phr) { *phr = pUnk->QueryInterface( IID_IGraphBuilder, reinterpret_cast(&pGB) ); if SUCCEEDED(*phr) { pGB->Release(); *phr = pUnk->QueryInterface( IID_IMediaControl, reinterpret_cast(&pMC) ); if SUCCEEDED(*phr) { pMC->Release(); *phr = pUnk->QueryInterface( IID_IMediaEvent, reinterpret_cast(&pME) ); if SUCCEEDED(*phr) { pME->Release(); *phr = pUnk->QueryInterface( IID_IMediaPosition, reinterpret_cast(&pMP) ); if SUCCEEDED(*phr) pMP->Release(); } } } } } CUnknown * WINAPI CAMovie::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr) { CUnknown * result = 0; result = new CAMovie( NAME("IAMovie Class"), pUnk, phr ); if ( !result ) *phr = E_OUTOFMEMORY; return result; } STDMETHODIMP CAMovie::NonDelegatingQueryInterface(REFIID iid, void ** ppv) { CheckPointer(ppv,E_POINTER); if ( iid == IID_IAMovie ) { return GetInterface(static_cast(this), ppv ); } else { return CUnknown::NonDelegatingQueryInterface(iid, ppv); } } // IFilterGraph (pGB) STDMETHODIMP CAMovie::AddFilter( IBaseFilter * pFilter, LPCWSTR pName ) { return pGB->AddFilter( pFilter, pName ); } STDMETHODIMP CAMovie::RemoveFilter( IBaseFilter * pFilter ) { return pGB->RemoveFilter( pFilter ); } STDMETHODIMP CAMovie::EnumFilters( IEnumFilters **ppEnum ) { return pGB->EnumFilters( ppEnum ); } STDMETHODIMP CAMovie::FindFilterByName( LPCWSTR pName, IBaseFilter ** ppFilter ) { return pGB->FindFilterByName( pName, ppFilter ); } STDMETHODIMP CAMovie::ConnectDirect( IPin * ppinOut, IPin * ppinIn, const AM_MEDIA_TYPE* pmt ) { return pGB->ConnectDirect( ppinOut, ppinIn, pmt ); } STDMETHODIMP CAMovie::Reconnect( IPin * ppin ) { return pGB->Reconnect( ppin ); } STDMETHODIMP CAMovie::Disconnect( IPin * ppin ) { return pGB->Disconnect( ppin ); } STDMETHODIMP CAMovie::SetDefaultSyncSource( ) { return pGB->SetDefaultSyncSource( ); } // IGraphBuilder (pGB) STDMETHODIMP CAMovie::Connect( IPin * ppinOut, IPin * ppinIn ) { return pGB->Connect( ppinOut, ppinIn ); } STDMETHODIMP CAMovie::Render( IPin * ppinOut ) { return pGB->Render( ppinOut ); } // This pair of methods are a cross between the IGraphBuilder ones and the IMediaControl ones, // unfortunate perhaps, but they do make the nicest C / C++ interfaces . STDMETHODIMP CAMovie::AddSourceFilter( LPCWSTR strFilename, IBaseFilter ** ppUnk ) { const LPWSTR fn = const_cast(strFilename); return pGB->AddSourceFilter( fn, fn, ppUnk ); } STDMETHODIMP CAMovie::RenderFile( LPCWSTR strFilename ) { const LPWSTR fn = const_cast(strFilename); return pGB->RenderFile( fn, fn ); } // IMediaControl (pMC) STDMETHODIMP CAMovie::Run( ) { return pMC->Run( ); } STDMETHODIMP CAMovie::Pause( ) { return pMC->Pause( ); } STDMETHODIMP CAMovie::Stop( ) { return pMC->Stop( ); } STDMETHODIMP CAMovie::GetState( LONG msTimeout, FILTER_STATE* pfs ) { return pMC->GetState( msTimeout, (long *)pfs ); } // IMediaEvent (pME) STDMETHODIMP CAMovie::GetEventHandle( HEVENT * hEvent ) { return pME->GetEventHandle( (long *)hEvent ); } STDMETHODIMP CAMovie::GetEvent( long * lEventCode, long * lParam1, long * lParam2, long msTimeout ) { return pME->GetEvent( lEventCode, lParam1, lParam2, msTimeout ); } STDMETHODIMP CAMovie::FreeEventParams( long lEventCode, long lParam1, long lParam2 ) { return pME->FreeEventParams( lEventCode, lParam1, lParam2 ); } STDMETHODIMP CAMovie::WaitForCompletion( long msTimeout, long * pEvCode ) { return pME->WaitForCompletion( msTimeout, pEvCode ); } STDMETHODIMP CAMovie::CancelDefaultHandling( long lEvCode ) { return pME->CancelDefaultHandling( lEvCode ); } STDMETHODIMP CAMovie::RestoreDefaultHandling( long lEvCode ) { return pME->RestoreDefaultHandling( lEvCode ); } // IMediaPosition (pMP ) STDMETHODIMP CAMovie::get_Duration( REFTIME* plength ) { return pMP->get_Duration( plength ); } STDMETHODIMP CAMovie::put_CurrentPosition( REFTIME llTime ) { return pMP->put_CurrentPosition( llTime ); } STDMETHODIMP CAMovie::get_CurrentPosition( REFTIME* pllTime ) { return pMP->get_CurrentPosition( pllTime ); } STDMETHODIMP CAMovie::get_StopTime( REFTIME* pllTime ) { return pMP->get_StopTime( pllTime ); } STDMETHODIMP CAMovie::put_StopTime( REFTIME llTime ) { return pMP->put_StopTime( llTime ); } STDMETHODIMP CAMovie::get_PrerollTime( REFTIME* pllTime ) { return pMP->get_PrerollTime( pllTime ); } STDMETHODIMP CAMovie::put_PrerollTime( REFTIME llTime ) { return pMP->put_PrerollTime( llTime ); } STDMETHODIMP CAMovie::put_Rate( double dRate ) { return pMP->put_Rate( dRate ); } STDMETHODIMP CAMovie::get_Rate( double * pdRate ) { return pMP->get_Rate( pdRate ); } // New methods // Remove all filters STDMETHODIMP CAMovie::RemoveAllFilters() { HRESULT hr; IEnumFilters * piEnumFilters; hr = EnumFilters( &piEnumFilters ); if SUCCEEDED(hr) { CGenericList FilterList("List of filters to be removed"); IBaseFilter * piFilter; while ( piEnumFilters->Next(1, &piFilter, 0) == NOERROR ) { FilterList.AddTail( piFilter ); } piEnumFilters->Release(); while ( piFilter = FilterList.RemoveHead() ) { hr = RemoveFilter( piFilter ); piFilter->Release(); } } return hr; } // If the filter graph can be Run to completion, run it and wait for it to complete STDMETHODIMP CAMovie::Play() { HRESULT hr; REFTIME duration; hr = get_Duration( &duration ); if ( SUCCEEDED( hr ) && duration > 0 ) { hr = Run(); if SUCCEEDED(hr) { long evCode; WaitForCompletion( INFINITE, &evCode ); } Stop(); } return hr; } STDMETHODIMP CAMovie::PlayFile( LPCWSTR strFilename ) { HRESULT hr; hr = RenderNewFile( strFilename ); if SUCCEEDED(hr) { hr = Play(); } return hr; } STDMETHODIMP CAMovie::RenderAll( ) { HRESULT hr = NOERROR; CImpIEnumPinsOutOverGraph EnumOutPins( this, &hr ); if SUCCEEDED(hr) { IPin * piPin; while ( EnumOutPins.Next( 1, &piPin, 0 ) == NOERROR ) { Render( piPin ); } } return hr; } STDMETHODIMP CAMovie::RenderNewFile( LPCWSTR strFilename ) { HRESULT hr; DisconnectAllAndRemoveIsolates(); hr = RenderFile( strFilename ); RemoveAllUnconnected(); return hr; } STDMETHODIMP CAMovie::EnumFiltersByInterface( REFIID iid, IEnumFilters ** ppEnum ) { HRESULT hr = NOERROR; *ppEnum = new CImpIEnumFilters( pGB, iid ); if (!*ppEnum) hr = E_OUTOFMEMORY; else if FAILED(hr) { delete *ppEnum; *ppEnum = 0; } return hr; } STDMETHODIMP CAMovie::EnumPins( IEnumPins ** ppEnum ) { HRESULT hr = NOERROR; *ppEnum = new CImpIEnumPinsOverGraph( pGB, &hr ); if (!*ppEnum) hr = E_OUTOFMEMORY; else if FAILED(hr) { delete *ppEnum; *ppEnum = 0; } return hr; } STDMETHODIMP CAMovie::EnumPinsIn( IEnumPins ** ppEnum ) { HRESULT hr = NOERROR; *ppEnum = new CImpIEnumPinsInOverGraph( pGB, &hr ); if (!*ppEnum) hr = E_OUTOFMEMORY; else if FAILED(hr) { delete *ppEnum; *ppEnum = 0; } return hr; } STDMETHODIMP CAMovie::EnumPinsOut( IEnumPins ** ppEnum ) { HRESULT hr = NOERROR; *ppEnum = new CImpIEnumPinsOutOverGraph( pGB, &hr ); if (!*ppEnum) hr = E_OUTOFMEMORY; else if FAILED(hr) { delete *ppEnum; *ppEnum = 0; } return hr; } // Protected methods // #include // #include // Disconnect all pins. If a filter has no pins, remove it completely // This is to part-destroy a filter graph, leaving only those filters // that may be usefult for rendering a file or pin after this function // has been called. HRESULT CAMovie::DisconnectAllAndRemoveIsolates() { // cout << "Pre DisconnectAllAndRemoveIsolates()\n" << pGB << endl; HRESULT hr; IEnumFilters * piEnumFilters; hr = EnumFilters( &piEnumFilters ); if SUCCEEDED(hr) { CGenericList PinList("List of pins to be disconnected"); CGenericList FilterList("List of filters to be removed"); IBaseFilter * piFilter; IPin * piPin; while ( piEnumFilters->Next(1, &piFilter, 0) == NOERROR ) { BOOL connected = FALSE, connectable = FALSE; IEnumPins * piEnumPins; hr = piFilter->EnumPins( &piEnumPins ); if SUCCEEDED(hr) { while ( piEnumPins->Next(1, &piPin, 0) == NOERROR ) { connectable = TRUE; IPin * piPinConnectedTo; piPin->ConnectedTo( &piPinConnectedTo ); if (piPinConnectedTo) { PinList.AddTail( piPin ); piPinConnectedTo->Release(); } else piPin->Release(); } piEnumPins->Release(); } if (!connectable) FilterList.AddTail( piFilter ); else piFilter->Release(); } piEnumFilters->Release(); while ( piPin = PinList.RemoveHead() ) { hr = Disconnect( piPin ); piPin->Release(); } while ( piFilter = FilterList.RemoveHead() ) { hr = RemoveFilter( piFilter ); piFilter->Release(); } } // cout << "Post DisconnectAllAndRemoveIsolates()\n" << pGB << endl; return hr; } // For all filters in graph, if the filter has pins, and all are unconnected, // then remove the filter from the graph. HRESULT CAMovie::RemoveAllUnconnected() { // cout << "Pre RemoveAllUnconnected()\n" << pGB << endl; HRESULT hr; IEnumFilters * piEnumFilters; hr = EnumFilters( &piEnumFilters ); if SUCCEEDED(hr) { CGenericList FilterList("List of filters to be removed"); IBaseFilter * piFilter; while ( piEnumFilters->Next(1, &piFilter, 0) == NOERROR ) { BOOL connected = FALSE, connectable = FALSE; IEnumPins * piEnumPins; hr = piFilter->EnumPins( &piEnumPins ); if SUCCEEDED(hr) { IPin * piPin; while ( !connected && piEnumPins->Next(1, &piPin, 0) == NOERROR ) { connectable = TRUE; IPin * piPinConnectedTo; piPin->ConnectedTo( &piPinConnectedTo ); if (piPinConnectedTo) { connected = TRUE; piPinConnectedTo->Release(); } piPin->Release(); } piEnumPins->Release(); } if (connectable && !connected) FilterList.AddTail( piFilter ); else piFilter->Release(); } piEnumFilters->Release(); while ( piFilter = FilterList.RemoveHead() ) { hr = RemoveFilter( piFilter ); piFilter->Release(); } } // cout << "Post RemoveAllUnconnected()\n" << pGB << endl; return hr; } //--------------------------------------------------------------------------- // // AMovieRegisterInterface() // // // //--------------------------------------------------------------------------- HRESULT AMovieRegisterInterface( CLSID ClsidInterface , LPTSTR strName , CLSID ClsidDistributor ) { TCHAR achKey[MAX_PATH]; TCHAR achClsidInterface[CHARS_IN_GUID]; TCHAR achClsidDistributor[CHARS_IN_GUID]; OLECHAR achTemp[CHARS_IN_GUID]; HKEY hkey, hsubkey; LONG lReturn; HRESULT hr; hr = StringFromGUID2( ClsidInterface , achTemp , CHARS_IN_GUID ); if( SUCCEEDED(hr) ) { wsprintf( achClsidInterface, "%ls", achTemp ); hr = StringFromGUID2( ClsidDistributor , achTemp , CHARS_IN_GUID ); } if( FAILED(hr) ) return hr; wsprintf( achClsidDistributor, "%ls", achTemp ); wsprintf( achKey, "Interface\\%s", achClsidInterface ); lReturn = RegCreateKey( HKEY_CLASSES_ROOT , achKey , &hkey ); if( ERROR_SUCCESS == lReturn ) { if( NULL != strName ) { RegSetValue( hkey , NULL , REG_SZ , strName , lstrlen(strName)+1 ); } lReturn = RegCreateKey( hkey , "Distributor" , &hsubkey ); if( ERROR_SUCCESS == lReturn ) { RegSetValue( hsubkey , NULL , REG_SZ , achClsidDistributor , lstrlen(achClsidDistributor)+1 ); RegCloseKey( hsubkey ); } RegCloseKey( hkey ); } return HRESULT_FROM_WIN32(lReturn); } //--------------------------------------------------------------------------- // // AMovieUnregisterInterface() // // // //--------------------------------------------------------------------------- HRESULT AMovieUnregisterInterface( REFCLSID ClsidInterface ) { HKEY hkey; LONG lReturn; HRESULT hr; TCHAR achClsidInterface[CHARS_IN_GUID]; OLECHAR achTemp[CHARS_IN_GUID]; // comvert CLSID to OLE string // hr = StringFromGUID2( ClsidInterface, achTemp, CHARS_IN_GUID ); if( FAILED(hr) ) return hr; // convert OLE string to ANSI (as that's // all that WIN95 Reg* calls can handle // wsprintf( achClsidInterface, "%ls", achTemp ); // open Interface key // lReturn = RegOpenKeyEx( HKEY_CLASSES_ROOT , TEXT("Interface") , 0 , MAXIMUM_ALLOWED , &hkey ); if( ERROR_SUCCESS == lReturn ) { // if successful, eliminate subkey and close // Interface key // EliminateSubKey( hkey, achClsidInterface ); RegCloseKey( hkey ); } return HRESULT_FROM_WIN32( lReturn ); } // // exported entry points for registration and // unregistration. // // In this case they (un)register out one and // only interface and then call through to the // default implementation. // // UUID Interface {359ace10-7688-11cf-8b23-00805f6cef60} // Interface name IAMovie // UUID Distributor {5f2759c0-7685-11cf-8b23-00805f6cef60} // STDAPI DllRegisterServer() { HRESULT hr = NOERROR; // Register interface so ActiveMovie can // find me from my IID // hr = AMovieRegisterInterface( IID_IAMovie , TEXT("IAMovie") , CLSID_AMovie ); if( SUCCEEDED(hr) ) hr = AMovieDllRegisterServer2( TRUE ); return hr; } STDAPI DllUnregisterServer() { HRESULT hr = NOERROR; hr = AMovieUnregisterInterface( IID_IAMovie ); if( SUCCEEDED(hr) ) hr = AMovieDllRegisterServer2( FALSE ); return hr; }