//==========================================================================; // // 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) 1996 - 1998 Microsoft Corporation. All Rights Reserved. // //--------------------------------------------------------------------------; /* Parser class - this class defines the object that actually splits out the data */ /* CBaseParser class This is the object that determines the nature of the data and actually splits the stream */ /* Simple stream reader class */ class CParseReader { public: /* Set the position */ virtual HRESULT Length(LONGLONG *pLength) = 0; virtual HRESULT SetPointer(LONGLONG) = 0; virtual HRESULT Read(PBYTE pbData, DWORD cbData) = 0; }; /* Parsing reader from CAsyncReader */ class CParseReaderFromAsync : public CParseReader { public: CParseReaderFromAsync(IAsyncReader *pRdr) : m_pReader(pRdr), m_llPos(0) {}; HRESULT Length(LONGLONG *pLength) { LONGLONG llAvailable; return m_pReader->Length(pLength, &llAvailable); } HRESULT SetPointer(LONGLONG llPos) { m_llPos = 0; return S_OK; } HRESULT Read(PBYTE pbData, DWORD cbData) { HRESULT hr = m_pReader->SyncRead(m_llPos, (LONG)cbData, pbData); if (S_OK == hr) { m_llPos += cbData; } return hr; } private: IAsyncReader *m_pReader; LONGLONG m_llPos; }; class CParserNotify; class CBaseParser { public: /* Instantiate a parser pNotify - to call back the calling object to create streams etc */ CBaseParser(CParserNotify *pNotify, HRESULT *phr) : m_pNotify(pNotify) {}; /* Initialize a parser pmt - type of stream if known - can be NULL pRdr - way to read the source medium - can be NULL */ virtual HRESULT Init( CParseReader *pRdr ) = 0; /* Get the size and count of buffers preferred based on the actual content */ virtual void GetSizeAndCount(LONG *plSize, LONG *plCount) = 0; /* Call this to reinitialize for a new stream */ virtual void StreamReset() = 0; /* Call this to pass new stream data : pbData - pointer to data lData - length of data plProcessed - Amount of data consumed */ virtual HRESULT Process( const BYTE *pbData, LONG lData, LONG *plProcessed ) = 0; protected: CParserNotify * const m_pNotify; }; /* Parser calls back to create streams and spit out buffers */ class CStreamNotify; class CParserNotify { public: /* Create an output stream with type *pmt, notifications to this stream passed to the **pStreamNotify object */ virtual HRESULT CreateStream( LPCWSTR pszName, CStreamNotify **pStreamNotify) = 0; }; class CStreamNotify { public: virtual HRESULT SendSample( const BYTE *pbData, LONG lData, REFERENCE_TIME rtStart, BOOL bSync ) = 0; /* Add a media type that's supported */ virtual HRESULT AddMediaType(CMediaType const *pmt) = 0; /* Return the current type */ virtual void CurrentMediaType(AM_MEDIA_TYPE *pmt) = 0; }; /* Splitter filter base classes */ /* Design : A splitter filter will have 1 input pin and multiple output pins CBaseSplitterFilter defines a base filter with 1 input pin and a list of output pins. This base class provides for pin enumeration, correct distribution of EndOfStream and handling of errors. The object structure is : IsA CBaseSplitterFilter <-------- CBaseFilter Allocators: This class relies on use CSequentialAllocator The input pin is designed around CPullPin which hooks up to IAsyncReader on the upstream output pin */ class CBaseSplitterFilter; class CBaseParser; /* Input pin stuff The input pin deletes all the output pins when it's disconnected On connection the output pins are created based on the media type and possibly on the file content The base class handles things like flushing and end of stream */ /* Special output pin type to handle lifetime */ class CSplitterOutputPin : public CBaseOutputPin { public: // Constructor CSplitterOutputPin( CBaseSplitterFilter *pFilter, HRESULT *phr, LPCWSTR pName); ~CSplitterOutputPin(); // CUnknown methods STDMETHODIMP_(ULONG) NonDelegatingAddRef(); STDMETHODIMP_(ULONG) NonDelegatingRelease(); // CBaseOutputPin methods - we just override these to do // our own hack allocator // override this to set the buffer size and count. Return an error // if the size/count is not to your liking virtual HRESULT DecideBufferSize( IMemAllocator * pAlloc, ALLOCATOR_PROPERTIES * pProp); // negotiate the allocator and its buffer size/count // calls DecideBufferSize to call SetCountAndSize virtual HRESULT DecideAllocator(IMemInputPin * pPin, IMemAllocator ** pAlloc); // override this to control the connection virtual HRESULT InitAllocator(IMemAllocator **ppAlloc); // Check the media type proposed HRESULT CheckMediaType(const CMediaType *); // returns the preferred formats for a pin HRESULT GetMediaType(int iPosition,CMediaType *pMediaType); // Send sample generated by parser HRESULT SendSample( const BYTE *pbData, LONG lData, REFERENCE_TIME rtStart, BOOL bSync ); /* Add a media type */ HRESULT AddMediaType( CMediaType const *pmt ); HRESULT DeliverEndOfStream() { m_pOutputQueue->EOS(); return S_OK; } // Delete our output queue on inactive HRESULT Inactive() { HRESULT hr = CBaseOutputPin::Inactive(); if (FAILED(hr)) { return hr; } delete m_pOutputQueue; m_pOutputQueue = NULL; return S_OK; } /* Wrapper to call output queue to flush samples */ void SendAnyway() { if (NULL != m_pOutputQueue) { m_pOutputQueue->SendAnyway(); } } // Override Active and EndFlush to set the discontinuity flag HRESULT Active() { m_bDiscontinuity = TRUE; /* If we're not connected we don't participate so it's OK */ if (!IsConnected()) { return S_OK; } HRESULT hr = CBaseOutputPin::Active(); if (FAILED(hr)) { return hr; } /* Create our batch list */ ASSERT(m_pOutputQueue == NULL); hr = S_OK; m_pOutputQueue = new COutputQueue(GetConnected(), // input pin &hr, // return code FALSE, // Auto detect TRUE, // ignored 50, // batch size TRUE, // exact batch 50); // queue size if (m_pOutputQueue == NULL) { return E_OUTOFMEMORY; } if (FAILED(hr)) { delete m_pOutputQueue; m_pOutputQueue = NULL; } return hr; } HRESULT DeliverBeginFlush() { /* We're already locked via the input pin */ m_pOutputQueue->BeginFlush(); return S_OK; } HRESULT DeliverEndFlush() { /* We're already locked via the input pin */ m_bDiscontinuity = TRUE; m_pOutputQueue->EndFlush(); return S_OK; } // Get our notify object CStreamNotify *GetNotify() { return &m_Notify; } protected: // Get a pointer to our allocator CSubAllocator *Allocator() { return (CSubAllocator *)m_pAllocator; } // Get a properly cast pointer to our filter CBaseSplitterFilter *Filter() { return (CBaseSplitterFilter *)m_pFilter; } protected: // Stream notify stuff class CImplStreamNotify : public CStreamNotify { public: CImplStreamNotify(CSplitterOutputPin *pPin) : m_pPin(pPin) {} HRESULT SendSample( const BYTE *pbData, LONG lData, REFERENCE_TIME rtStart, BOOL bSync ) { return m_pPin->SendSample(pbData, lData, rtStart, bSync); } HRESULT AddMediaType(CMediaType const *pmt) { return m_pPin->AddMediaType(pmt); } void CurrentMediaType(AM_MEDIA_TYPE *pmt) { m_pPin->ConnectionMediaType(pmt); } private: CSplitterOutputPin * const m_pPin; }; CImplStreamNotify m_Notify; // Remember when to send NewSegment and discontinuity */ BOOL m_bDiscontinuity; // Output queue COutputQueue *m_pOutputQueue; // List of media types we support CGenericList m_lTypes; }; /* Base CSplitterInputPin on CPullPin */ class CSplitterInputPin : public CBaseInputPin { public: CSplitterInputPin( CBaseSplitterFilter *pFilter, HRESULT *phr); /* NonDelegating IUnknown methods - we don't support IMemInputPin */ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv) { if (riid == IID_IMemInputPin) { return E_NOINTERFACE; } return CBaseInputPin::NonDelegatingQueryInterface(riid, ppv); } /* IPin methods */ STDMETHODIMP EndOfStream(); STDMETHODIMP BeginFlush(); STDMETHODIMP EndFlush(); STDMETHODIMP Receive(IMediaSample *pSample); /* IMemInputPin methods */ /* Where we're told which allocator we are using */ STDMETHODIMP NotifyAllocator(IMemAllocator *pAllocator) { if (pAllocator != (IMemAllocator *)m_pAllocator) { return E_FAIL; } else { return S_OK; } } /* Say if we're blocking */ STDMETHODIMP ReceiveCanBlock() { return S_FALSE; } /* CBasePin methods */ HRESULT BreakConnect(); // Override to release puller HRESULT CheckConnect(IPin *pPin); // Override to connect to puller HRESULT Active(); HRESULT Inactive(); HRESULT CheckMediaType(const CMediaType *pmt); HRESULT CompleteConnect(IPin *pReceivePin); /* Report filter from reader */ void NotifyError(HRESULT hr) { if (FAILED(hr)) { m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0); } EndOfStream(); }; /* Convenient way to get the allocator */ CSequentialAllocator *Allocator() { return (CSequentialAllocator *)m_pAllocator; } /* Point to our media type */ CMediaType *MediaType() { return &m_mt; } /* Return our async reader */ IAsyncReader *Reader() { IAsyncReader *pReader = m_puller.GetReader(); pReader->Release(); return pReader; } private: // Get a properly case pointer to our filter CBaseSplitterFilter *Filter() { return (CBaseSplitterFilter *)m_pFilter; } // class to pull data from IAsyncReader if we detect that interface // on the output pin class CImplPullPin : public CPullPin { // forward everything to containing pin CSplitterInputPin * const m_pPin; public: CImplPullPin(CSplitterInputPin* pPin) : m_pPin(pPin) { }; // forward this to the pin's IMemInputPin::Receive HRESULT Receive(IMediaSample* pSample) { return m_pPin->Receive(pSample); }; // override this to handle end-of-stream HRESULT EndOfStream(void) { return m_pPin->EndOfStream(); }; // these errors have already been reported to the filtergraph // by the upstream filter so ignore them void OnError(HRESULT hr) { // ignore VFW_E_WRONG_STATE since this happens normally // during stopping and seeking if (hr != VFW_E_WRONG_STATE) { m_pPin->NotifyError(hr); } }; // flush the pin and all downstream HRESULT BeginFlush() { return m_pPin->BeginFlush(); }; // Tell the next guy we've finished flushing HRESULT EndFlush() { return m_pPin->EndFlush(); }; }; CImplPullPin m_puller; }; class CBaseSplitterFilter : public CBaseFilter { friend class CSplitterOutputPin; friend class CSplitterInputPin; public: // Constructor and destructor CBaseSplitterFilter( TCHAR *pName, LPUNKNOWN pUnk, REFCLSID rclsid, HRESULT *phr); ~CBaseSplitterFilter(); // IMediaFilter methods - override these to manage locking STDMETHODIMP Stop(); STDMETHODIMP Pause(); /* Stream control stuff */ virtual HRESULT BeginFlush(); virtual HRESULT EndFlush(); virtual void EndOfStream(); virtual HRESULT Receive(IMediaSample *pSample); virtual HRESULT CheckInputType(const CMediaType *pmt) = 0; virtual HRESULT CompleteConnect(IPin *pReceivePin); // Create the parser virtual CBaseParser *CreateParser( CParserNotify *pNotify, CMediaType *pType) = 0; // CBaseFilter methods int GetPinCount(); // 0 pins CBasePin *GetPin(int iPin); /* Add a new output pin */ BOOL AddOutputPin(CSplitterOutputPin *pOutputPin); /* Destroy the output pins */ void DestroyOutputPins(); /* Destroy the input pin */ void DestroyInputPin(); /* Notify to create a new stream Called back through CParseNotify */ HRESULT CreateStream( LPCWSTR pszName, CStreamNotify **ppStreamNotify ); /* Called when BreakConnect called on the input pin */ virtual void BreakConnect(); protected: /* Utility to get the allocator */ CSequentialAllocator *Allocator() { return InputPin()->Allocator(); } /* Reset the states of the allocator and parser ready to receive a new stream */ void ResetAllocatorAndParser() { Allocator()->Flush(); m_pParser->StreamReset(); } CSplitterInputPin *InputPin() { return (CSplitterInputPin *)m_pInput; } protected: // Our input pin - created by derived classes CBaseInputPin *m_pInput; // Our output pin list - Derived classes append to this CGenericList m_OutputPins; // Filter locking CCritSec m_csFilter; // Streaming lock CCritSec m_csStream; // Pin database lock CCritSec m_csPins; // Parser - created when we're connected CBaseParser *m_pParser; // Parser notify - use member variable to avoid mulitple inheritance class CImplParserNotify : public CParserNotify { public: CImplParserNotify(CBaseSplitterFilter *pFilter) : m_pFilter(pFilter) {}; HRESULT CreateStream( LPCWSTR pszName, CStreamNotify **ppStreamNotify ) { return m_pFilter->CreateStream(pszName, ppStreamNotify); } private: CBaseSplitterFilter * const m_pFilter; }; CImplParserNotify m_Notify; };