//==========================================================================;
//
// 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 - 1997 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<CMediaType> 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<CSplitterOutputPin> 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;
};