Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1162 lines
30 KiB

/*
Copyright (c) 1998-1999 Microsoft Corporation
*/
#ifndef __MEDIA_TERMINAL_FILTER__
#define __MEDIA_TERMINAL_FILTER__
// include header files for the amovie types
#include "Stream.h"
#include "Sample.h"
// number of internal buffers allocated by default
// (for write terminal)
const DWORD DEFAULT_AM_MST_NUM_BUFFERS = 5;
// while this is a LONG, it should actually be a positive value that'll
// fit in a LONG (the buffer size and data size variables of the sample)
// are LONG, so this is long as well
const LONG DEFAULT_AM_MST_SAMPLE_SIZE = 640;
// alignment of buffers allocated
const LONG DEFAULT_AM_MST_BUFFER_ALIGNMENT = 1;
// number of prefix bytes in buffers allocated
const LONG DEFAULT_AM_MST_BUFFER_PREFIX = 0;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// CNBQueue
//
// Non blocking version of active movie queue class. Very basic Q built
// entirely on Win32.
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
template <class T> class CNBQueue {
private:
HANDLE hSemPut; // Semaphore controlling queue "putting"
HANDLE hSemGet; // Semaphore controlling queue "getting"
CRITICAL_SECTION CritSect; // Thread seriallization
int nMax; // Max objects allowed in queue
int iNextPut; // Array index of next "PutMsg"
int iNextGet; // Array index of next "GetMsg"
T **QueueObjects; // Array of objects (ptr's to void)
public:
BOOL InitializeQ(int n)
{
LOG((MSP_TRACE, "CNBQueue::InitializeQ[%p] - enter", this));
//
// the argument had better be valid
//
if (0 > n)
{
TM_ASSERT(FALSE);
return FALSE;
}
if (QueueObjects != NULL)
{
//
// already initialized. this is a bug.
//
TM_ASSERT(FALSE);
return FALSE;
}
iNextPut = 0;
iNextGet = 0;
//
// attempt to create critical section
//
try
{
InitializeCriticalSection(&CritSect);
}
catch(...)
{
//
// failed to create critical section
//
LOG((MSP_ERROR, "CNBQueue::InitializeQ - failed to initialize critical section"));
return FALSE;
}
//
// attempt to create a semaphore
//
TCHAR *ptczSemaphoreName = NULL;
#if DBG
//
// in debug build, use named semaphores.
//
TCHAR tszPutSemaphoreName[MAX_PATH];
_stprintf(tszPutSemaphoreName,
_T("CNBQueuePutSemaphore_pid[0x%lx]_CNBQueue[%p]_"),
GetCurrentProcessId(), this);
LOG((MSP_TRACE, "CNBQueue::InitializeQ - creating put semaphore [%S]",
tszPutSemaphoreName));
ptczSemaphoreName = &tszPutSemaphoreName[0];
#endif
hSemPut = CreateSemaphore(NULL, n, n, ptczSemaphoreName);
if (NULL == hSemPut)
{
//
// cleanup and exit
//
DeleteCriticalSection(&CritSect);
LOG((MSP_ERROR, "CNBQueue::InitializeQ - failed to create put semaphore"));
return FALSE;
}
#if DBG
//
// in debug build, use named semaphores.
//
TCHAR tszGetSemaphoreName[MAX_PATH];
_stprintf(tszGetSemaphoreName,
_T("CNBQueueGetSemaphore_pid[0x%lx]_CNBQueue[%p]_"),
GetCurrentProcessId(), this);
LOG((MSP_TRACE, "CNBQueue::InitializeQ - creating get semaphore [%S]",
tszGetSemaphoreName));
ptczSemaphoreName = &tszGetSemaphoreName[0];
#endif
hSemGet = CreateSemaphore(NULL, 0, n, ptczSemaphoreName);
if (NULL == hSemGet)
{
//
// cleanup and exit
//
CloseHandle(hSemPut);
hSemPut = NULL;
DeleteCriticalSection(&CritSect);
LOG((MSP_ERROR, "CNBQueue::InitializeQ - failed to create get semaphore"));
return FALSE;
}
//
// attempt to allocate queue
//
QueueObjects = new T*[n];
if (NULL == QueueObjects)
{
//
// cleanup and exit
//
CloseHandle(hSemPut);
hSemPut = NULL;
CloseHandle(hSemGet);
hSemGet = NULL;
DeleteCriticalSection(&CritSect);
LOG((MSP_ERROR, "CNBQueue::InitializeQ - failed to allocate queue objects"));
return FALSE;
}
nMax = n;
LOG((MSP_TRACE, "CNBQueue::InitializeQ - exit"));
return TRUE;
}
void ShutdownQ()
{
//
// QueueObjects also doubles as "Object Initialized" flag
//
// if object is initialized, _all_ its resource data members must
// be released
//
if (NULL != QueueObjects)
{
delete [] QueueObjects;
QueueObjects = NULL;
DeleteCriticalSection(&CritSect);
CloseHandle(hSemPut);
hSemPut = NULL;
CloseHandle(hSemGet);
hSemGet = NULL;
}
}
public:
CNBQueue()
: QueueObjects(NULL),
hSemPut(NULL),
hSemGet(NULL),
iNextPut(0),
iNextGet(0),
nMax(0)
{}
~CNBQueue()
{
//
// deallocate resources if needed
//
ShutdownQ();
}
T *DeQueue(BOOL fBlock = TRUE)
{
if (NULL == QueueObjects)
{
//
// the queue is not initialized
//
return NULL;
}
//
// block as needed
//
if (fBlock)
{
DWORD dwr = WaitForSingleObject(hSemGet, INFINITE);
if ( WAIT_OBJECT_0 != dwr)
{
//
// something's wrong
//
return NULL;
}
}
else
{
//
// Check for something on the queue but don't wait. If there
// is nothing in the queue then we'll let the caller deal with
// it.
//
DWORD dwr = WaitForSingleObject(hSemGet, 0);
if (dwr == WAIT_TIMEOUT)
{
return NULL;
}
}
//
// get an object from the queue
//
EnterCriticalSection(&CritSect);
int iSlot = iNextGet++ % nMax;
T *pObject = QueueObjects[iSlot];
LeaveCriticalSection(&CritSect);
// Release anyone waiting to put an object onto our queue as there
// is now space available in the queue.
//
ReleaseSemaphore(hSemPut, 1L, NULL);
return pObject;
}
BOOL EnQueue(T *pObject)
{
if (NULL == QueueObjects)
{
//
// the queue is not initialized
//
return FALSE;
}
// Wait for someone to get something from our queue, returns straight
// away is there is already an empty slot on the queue.
//
DWORD dwr = WaitForSingleObject(hSemPut, INFINITE);
if ( WAIT_OBJECT_0 != dwr)
{
//
// something's wrong
//
return FALSE;
}
EnterCriticalSection(&CritSect);
int iSlot = iNextPut++ % nMax;
QueueObjects[iSlot] = pObject;
LeaveCriticalSection(&CritSect);
// Release anyone waiting to remove an object from our queue as there
// is now an object available to be removed.
//
ReleaseSemaphore(hSemGet, 1L, NULL);
return TRUE;
}
};
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// define class CTMStreamSample - this is used by CMediaTerminalFilter
// currently, the actual buffer used by the sample is created dynamically on
// the heap and when the sample is destroyed the buffer is also destroyed
// this may be changed to using a fixed size buffer pool in future
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
class CTMStreamSample : public CSample
{
friend class CMediaTerminalFilter;
public:
inline CTMStreamSample();
// needs to be virtual, or the derived classes' destructor may not
// be called when a CTMStreamSample * is deleted
virtual ~CTMStreamSample()
{}
// calls CSample::InitSample(pStream, bIsInternalSample)
// sets member variables
HRESULT Init(
CStream &Stream,
bool bIsInternalSample,
PBYTE pBuffer,
LONG BufferSize
);
inline void SetBufferInfo(
DWORD BufferSize,
BYTE *pBuffer,
DWORD DataSize
);
inline void GetBufferInfo(
DWORD &BufferSize,
BYTE *&pBuffer,
DWORD &DataSize
);
// copy the contents of the src media sample into this instance
// CSample::CopyFrom doesn't set time (start/stop) valid flags
// this fixes the problem.
void CopyFrom(
IN IMediaSample *pSrcMediaSample
);
protected:
PBYTE m_pBuffer;
LONG m_BufferSize;
LONG m_DataSize;
private:
// Methods forwarded from MediaSample object.
HRESULT MSCallback_GetPointer(BYTE ** ppBuffer) { *ppBuffer = m_pBuffer; return NOERROR; }
LONG MSCallback_GetSize(void) { return m_BufferSize; }
LONG MSCallback_GetActualDataLength(void) { return m_DataSize; }
HRESULT MSCallback_SetActualDataLength(LONG lActual)
{
if (lActual <= m_BufferSize) {
m_DataSize = lActual;
return NOERROR;
}
return E_INVALIDARG;
};
};
inline
CTMStreamSample::CTMStreamSample(
)
: m_pBuffer(NULL),
m_BufferSize(0),
m_DataSize(0)
{
}
inline void
CTMStreamSample::SetBufferInfo(
DWORD BufferSize,
BYTE *pBuffer,
DWORD DataSize
)
{
m_BufferSize = BufferSize;
m_pBuffer = pBuffer;
m_DataSize = DataSize;
}
inline void
CTMStreamSample::GetBufferInfo(
DWORD &BufferSize,
BYTE *&pBuffer,
DWORD &DataSize
)
{
BufferSize = m_BufferSize;
pBuffer = m_pBuffer;
DataSize = m_DataSize;
}
class CQueueMediaSample : public CTMStreamSample
{
public:
inline CQueueMediaSample();
#if DBG
virtual ~CQueueMediaSample();
#endif // DBG
// calls CTMStreamSample::Init, sets members
HRESULT Init(
IN CStream &pStream,
IN CNBQueue<CQueueMediaSample> &pQueue
);
void HoldFragment(
IN DWORD FragSize,
IN BYTE *pbData,
IN IMediaSample &FragMediaSample
);
inline DWORD GetDataSize() { return m_DataSize; }
protected:
// pointer to a queue that contains us!
CNBQueue<CQueueMediaSample> *m_pSampleQueue;
// ptr to the sample being fragmented
CComPtr<IMediaSample> m_pFragMediaSample;
// Overridden to provide different behavior
void FinalMediaSampleRelease();
};
inline
CQueueMediaSample::CQueueMediaSample(
)
: m_pSampleQueue(NULL)
{
}
class CUserMediaSample :
protected CTMStreamSample,
public IMemoryData,
public ITAMMediaFormat
{
public:
BEGIN_COM_MAP(CUserMediaSample)
COM_INTERFACE_ENTRY2(IUnknown, IStreamSample)
COM_INTERFACE_ENTRY(IStreamSample)
COM_INTERFACE_ENTRY(IMemoryData)
COM_INTERFACE_ENTRY(ITAMMediaFormat)
COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pFTM)
END_COM_MAP()
inline CUserMediaSample();
virtual ~CUserMediaSample();
// if asked to allocate buffers, verify allocator properties
static BOOL VerifyAllocatorProperties(
IN BOOL bAllocateBuffers,
IN const ALLOCATOR_PROPERTIES &AllocProps
);
// calls CTMStreamSample::Init, sets members
HRESULT Init(
IN CStream &Stream,
IN BOOL bAllocateBuffer,
IN DWORD ReqdBufferSize,
IN const ALLOCATOR_PROPERTIES &AllocProps
);
void BeginFragment(
IN BOOL bNoteCurrentTime
);
// assign fragment to CQueueMediaSample
void Fragment(
IN BOOL bFragment,
IN LONG AllocBufferSize,
IN OUT CQueueMediaSample &QueueMediaSample,
OUT BOOL &bDone
);
// copy fragment to downstream allocator's IMediaSample
HRESULT CopyFragment(
IN BOOL bFragment,
IN LONG AllocBufferSize,
IN OUT IMediaSample * pDestMediaSample,
OUT BOOL & bDone
);
// computes the time to wait. it checks the time at which the last
// fragmented byte would be due and determines the time to wait using
// the time delay since the beginning of fragmentation
DWORD GetTimeToWait(
IN DOUBLE DelayPerByte
);
// when we are decommitted/aborted while being fragmented, we
// need to get rid of our refcnt on internal IMediaSample and set
// the error code to E_ABORT. this will be signaled to the user
// only when the last refcnt on IMediaSample is released
// (possibly by an outstanding queue sample)
void AbortDuringFragmentation();
// copy the contents of the src media sample into this instance
HRESULT CopyFrom(
IN IMediaSample *pSrcMediaSample
);
HRESULT CopyFrom(
IN IMediaSample *pSrcMediaSample,
IN OUT BYTE *&pBuffer,
IN OUT LONG &DataLength
);
// over-ridden to check if the instance is committed before
// adding the sample to the CStream buffer pool
virtual HRESULT SetCompletionStatus(HRESULT hrCompletionStatus);
// IStreamSample
// this method is over-ridden from the base class so that we can
// decrement the refcnt on a sample if stealing it from the CStream
// free buffer pool is successful
STDMETHODIMP CompletionStatus(
IN DWORD dwFlags,
IN /* [optional] */ DWORD dwMilliseconds
);
// IMemoryData
STDMETHOD(SetBuffer)(
IN DWORD cbSize,
IN BYTE * pbData,
IN DWORD dwFlags
);
STDMETHOD(GetInfo)(
OUT DWORD *pdwLength,
OUT BYTE **ppbData,
OUT DWORD *pcbActualData
);
STDMETHOD(SetActual)(
IN DWORD cbDataValid
);
// ITAMMediaFormat
// redirect this call to ((CMediaTerminalFilter *)m_pStream)
STDMETHOD(get_MediaFormat)(
OUT /* [optional] */ AM_MEDIA_TYPE **ppFormat
);
// this is not allowed
STDMETHOD(put_MediaFormat)(
IN const AM_MEDIA_TYPE *pFormat
);
protected:
// marshaller
IUnknown *m_pFTM;
// TRUE if we allocated the buffer (then, we need to destroy it too)
BOOL m_bWeAllocatedBuffer;
// time at which BeginFragment was called (value returned
// by timeGetTime)
DWORD m_BeginFragmentTime;
// these many bytes of the buffer have already been fragmented
LONG m_NumBytesFragmented;
// TRUE if being fragmented
BOOL m_bBeingFragmented;
// size of the buffer that the application will have to provide, if app
// does its own memory allocation
DWORD m_dwRequiredBufferSize;
// this calls the base class FinalMediaSampleRelease and
// then releases reference to self obtained in BeginFragment
virtual void FinalMediaSampleRelease();
private:
virtual HRESULT InternalUpdate(
DWORD dwFlags,
HANDLE hEvent,
PAPCFUNC pfnAPC,
DWORD_PTR dwptrAPCData
);
};
inline
CUserMediaSample::CUserMediaSample(
)
: m_bWeAllocatedBuffer(FALSE),
m_NumBytesFragmented(0),
m_bBeingFragmented(FALSE),
m_BeginFragmentTime(0),
m_dwRequiredBufferSize(0)
{
// can fail
CoCreateFreeThreadedMarshaler(
GetControllingUnknown(),
&m_pFTM
);
}
inline
CUserMediaSample::~CUserMediaSample(
)
{
if (m_bWeAllocatedBuffer)
{
if (NULL != m_pBuffer)
{
delete m_pBuffer;
}
}
// if there is an outstanding APC call and the user handle
// (the targe thread handle) has not been closed, close it
if ((NULL != m_UserAPC) && (NULL != m_hUserHandle))
{
CloseHandle(m_hUserHandle);
}
if (NULL != m_pFTM)
{
m_pFTM->Release();
m_pFTM = NULL;
}
}
/* The media stream terminal filter */
// uses class CMediaPumpPool
class CMediaPumpPool;
// friend
class CMediaTerminal;
class CMediaTerminalFilter :
public CStream,
public ITAllocatorProperties
{
friend CMediaTerminal;
public:
DECLARE_AGGREGATABLE(CMediaTerminalFilter)
DECLARE_GET_CONTROLLING_UNKNOWN()
BEGIN_COM_MAP(CMediaTerminalFilter)
COM_INTERFACE_ENTRY(ITAllocatorProperties)
COM_INTERFACE_ENTRY_CHAIN(CStream)
END_COM_MAP()
// set the member variables
inline CMediaTerminalFilter();
virtual ~CMediaTerminalFilter();
// calls the IAMMediaStream::Initialize(NULL, 0, PurposeId, StreamType),
// sets certain member variables
// ex. m_pAmovieMajorType
virtual HRESULT Init(
IN REFMSPID PurposeId,
IN const STREAM_TYPE StreamType,
IN const GUID &AmovieMajorType
);
// the thread pump calls the filter back during the registration
// to tell it that registration succeeded and that the pump will be
// waiting on the m_hWaitFreeSem handle
HRESULT SignalRegisteredAtPump();
// this method only makes sense for a write terminal and is used by CMediaPump
// to obtain a filled buffer for passing downstream
virtual HRESULT GetFilledBuffer(
OUT IMediaSample *&pMediaSample,
OUT DWORD &WaitTime
);
// the caller is supposed to call DeleteMediaType(*ppmt) (on success)
HRESULT GetFormat(
OUT AM_MEDIA_TYPE **ppmt
);
// This method can only be called after initialization when the stream
// is not connected. It can only be called if the stream is writeable.
// it is used in writeable filters to set the media format to negotiate
// when connected to the filter graph.
HRESULT SetFormat(
IN AM_MEDIA_TYPE *pmt
);
// checks if the filter is committed before adding the sample
// to the CStream buffer pool
HRESULT AddToPoolIfCommitted(
IN CSample *pSample
);
// first check if this sample is the one being fragmented currently,
// then check the free pool
BOOL StealSample(
IN CSample *pSample
);
// ITAllocatorProperties -
// exposes the allocator properties of the Media Streaming Terminal
// (MST) to a user. A user only needs to use this interface when he
// needs to use his own buffers or needs to operate with a fixed set
// of samples
// this method may only be called before connection and will
// force the MST to use these values during filter negotiation
// if the connecting filter doesn't accept these, the connection
// shall not be established
STDMETHOD(SetAllocatorProperties)(
IN ALLOCATOR_PROPERTIES *pAllocProperties
);
// gets current values for the allocator properties
// after connection, this provides the negotiated values
// it is invalid before connection. The MST will accept
// any values suggested by the filters it connects to
STDMETHOD(GetAllocatorProperties)(
OUT ALLOCATOR_PROPERTIES *pAllocProperties
);
// TRUE by default. when set to FALSE, the sample allocated
// by the MST don't have any buffers and they must be supplied
// before Update is called on the samples
STDMETHOD(SetAllocateBuffers)(
IN BOOL bAllocBuffers
);
// returns the current value of this boolean configuration parameter
STDMETHOD(GetAllocateBuffers)(
OUT BOOL *pbAllocBuffers
);
// this size is used for allocating buffers when AllocateSample is
// called. this is only valid when we have been told to allocate buffers
STDMETHOD(SetBufferSize)(
IN DWORD BufferSize
);
// returns the value used to allocate buffers when AllocateSample is
// called. this is only valid when we have been told to allocate buffers
STDMETHOD(GetBufferSize)(
OUT DWORD *pBufferSize
);
// over-ridden base class methods
// CStream
// IAMMediaStream
// over-ride this to return failure. we don't allow it to join a multi-media
// stream because the multi-media stream thinks it owns the stream
STDMETHOD(JoinAMMultiMediaStream)(
IN IAMMultiMediaStream *pAMMultiMediaStream
);
// over-ride this to return failure if the filter is anything other than the internally
// created filter. The internally created media stream filter has only one IAMMediaStream
// (this one) in it
STDMETHOD(JoinFilter)(
IN IMediaStreamFilter *pMediaStreamFilter
);
STDMETHOD(AllocateSample)(
IN DWORD dwFlags,
OUT IStreamSample **ppSample
);
STDMETHOD(CreateSharedSample)(
IN IStreamSample *pExistingSample,
IN DWORD dwFlags,
OUT IStreamSample **ppNewSample
);
STDMETHOD(SetSameFormat)(
IN IMediaStream *pStream,
IN DWORD dwFlags
);
// CStream over-ride - this method had to be replaced because
// of references to CPump which itself is being replaced by CMediaPump
STDMETHODIMP SetState(
IN FILTER_STATE State
);
// CStream - end
// IMemInputPin
STDMETHOD(GetAllocatorRequirements)(
IN ALLOCATOR_PROPERTIES*pProps
);
STDMETHOD(Receive)(
IN IMediaSample *pSample
);
// supports IAMBufferNegotiation interface on TERMINAL
// this is necessary because ITAllocatorProperties also
// has an identical GetAllocatorProperties method!
STDMETHOD(SuggestAllocatorProperties)(
IN const ALLOCATOR_PROPERTIES *pProperties
);
// IMemAllocator
STDMETHOD(GetBuffer)(IMediaSample **ppBuffer, REFERENCE_TIME * pStartTime,
REFERENCE_TIME * pEndTime, DWORD dwFlags);
// ** figure out what needs to be done for this allocator interface
// since the number of buffers that can be created is unbounded
STDMETHOD(SetProperties)(ALLOCATOR_PROPERTIES* pRequest, ALLOCATOR_PROPERTIES* pActual);
STDMETHOD(GetProperties)(ALLOCATOR_PROPERTIES* pProps);
STDMETHOD(Commit)();
STDMETHOD(Decommit)();
// IPin
STDMETHOD(Connect)(IPin * pReceivePin, const AM_MEDIA_TYPE *pmt);
STDMETHOD(ReceiveConnection)(IPin * pConnector, const AM_MEDIA_TYPE *pmt);
// the base class implementation doesn't validate the parameter
STDMETHOD(ConnectionMediaType)(AM_MEDIA_TYPE *pmt);
// should accept all media types which match the major type corresponding to the purpose id
STDMETHOD(QueryAccept)(const AM_MEDIA_TYPE *pmt);
// over-ridden from CStream to set the end of stream flag to false
// this is done instead of setting it in Connect and ReceiveConnection
STDMETHODIMP Disconnect();
//
// this is called by media pump when it has a sample for us to process
//
STDMETHODIMP ProcessSample(IMediaSample *pSample);
protected:
// last sample ended at this (calculated) time
REFERENCE_TIME m_rtLastSampleEndedAt;
//
// calculated duration of the sample that was last submitted
//
REFERENCE_TIME m_rtLastSampleDuration;
//
// real (measured) time of submission of the last sample
//
REFERENCE_TIME m_rtRealTimeOfLastSample;
// flag to check if this is an audio filter, the CStream member
// m_PurposeId is a guiid and this just provides a less expensive
// way of checking the same thing
BOOL m_bIsAudio;
// contains the samples that will be passed to downstream filters.
CNBQueue<CQueueMediaSample> m_SampleQueue;
// These datamembers provide some fragmentation support
// for buffers going downstream
CUserMediaSample *m_pSampleBeingFragmented;
// flag for allocating buffers for samples when AllocateSample is
// called. Its TRUE by default, but the user can set it before
// connection
BOOL m_bAllocateBuffers;
// size of buffers to allocate in AllocateSample if m_bAllocateBuffers
// is TRUE. if this isn't set (i.e. set to 0), the negotiated
// allocator properties buffer size is used in its place
DWORD m_AllocateSampleBufferSize;
// FALSE by default. This is set to TRUE if the user specifies
// allocator properties for them to see.
// (we used to insist on our own allocator properties when this
// was TRUE, but now this just means that we need to translate
// between disjoint buffer sizes if needed)
BOOL m_bUserAllocProps;
ALLOCATOR_PROPERTIES m_UserAllocProps;
// allocator properties negotiated -- if none suggested (by msp) and
// none requested by user, we use whatever the other filter has
BOOL m_bSuggestedAllocProps;
ALLOCATOR_PROPERTIES m_AllocProps;
// per byte delay for audio samples - only valid for write filter
DOUBLE m_AudioDelayPerByte;
// per frame delay for video samples - only valid for write filter
DWORD m_VideoDelayPerFrame;
// the filter restricts the acceptable media types to those that match the major type
// this corresponds to the purpose id of the IAMMediaStream (set in Init)
const GUID *m_pAmovieMajorType;
// this is the media type suggested by a user of the terminal
// it is only valid for writeable streams if put_MediaType was called
// (i.e. not valid for readable streams)
// this needs to be freed in the destructor
// cstream - cbaseterm gets\sets it through methods
AM_MEDIA_TYPE *m_pSuggestedMediaType;
// this pump replaces the CStream related implementation of CPump
// CPump uses a separate thread for each write terminal,
// it uses IMemAllocator::GetBuffer to get a user written media
// sample (for passing on downstream). This method should only be
// used to get the next free buffer to write into.
// CStream methods
// this is only used during Connect and ReceiveConnect to supply the optional media type
// since we over-ride Connect and ReceiveConnection methods, this should never get called
virtual HRESULT GetMediaType(ULONG Index, AM_MEDIA_TYPE **ppMediaType);
// others
// sets the time to delay - per byte for audio, per frame for video
void GetTimingInfo(
IN const AM_MEDIA_TYPE &MediaType
);
// timestamps the sample
HRESULT SetTime(
IN IMediaSample *pMediaSample
);
// set discontinuity flag on the sample it the sample came too late -- we
// assume that if the application stopped feeding mst with data, this is
// because there was a gap in the actual data flow
HRESULT SetDiscontinuityIfNeeded(
IN IMediaSample *pMediaSample
);
// set the default allocator properties
void SetDefaultAllocatorProperties();
//
// Helper methods for GetFilledBuffer.
//
virtual HRESULT FillDownstreamAllocatorBuffer(
OUT IMediaSample *& pMediaSample,
OUT DWORD & WaitTime,
OUT BOOL * pfDone
);
virtual HRESULT FillMyBuffer(
OUT IMediaSample *& pMediaSample,
OUT DWORD & WaitTime,
OUT BOOL * pfDone
);
private :
// this is a weak reference and should not be a CComPtr
// this tells us that we should only accept this media stream filter
// when a non-null value is proposed in JoinFilter
IMediaStreamFilter *m_pMediaStreamFilterToAccept;
// sets the media stream filter that may be acceptable
inline void SetMediaStreamFilter(
IN IMediaStreamFilter *pMediaStreamFilter
)
{
m_pMediaStreamFilterToAccept = pMediaStreamFilter;
}
public:
// implements single thread pump for all write terminal filters
// it uses GetFilledBuffer to obtain filled samples to write downstream
// and to detect when to remove this filter from its list of filters to
// service
// ZoltanS: must be public so we can access it in DllMain
// ZoltanS: no longer single thread pump; it is a wrapper which delegated
// to one or more single thread pumps
static CMediaPumpPool ms_MediaPumpPool;
};
// set the member variables
inline
CMediaTerminalFilter::CMediaTerminalFilter(
)
: m_bIsAudio(TRUE),
m_bAllocateBuffers(TRUE),
m_AllocateSampleBufferSize(0),
m_bUserAllocProps(FALSE),
m_bSuggestedAllocProps(FALSE),
m_AudioDelayPerByte(0),
m_VideoDelayPerFrame(0),
m_pAmovieMajorType(NULL),
m_pSuggestedMediaType(NULL),
m_pSampleBeingFragmented(NULL),
m_pMediaStreamFilterToAccept(NULL),
m_rtLastSampleEndedAt(0),
m_rtLastSampleDuration(0),
m_rtRealTimeOfLastSample(0)
{
}
#endif // __MEDIA_TERMINAL_FILTER__