amfilter.cpp
资源名称:p2p_vod.rar [点击查看]
上传用户:liguizhu
上传日期:2015-11-01
资源大小:2422k
文件大小:149k
源码类别:
P2P编程
开发平台:
Visual C++
- HRESULT
- CBaseOutputPin::Deliver(IMediaSample * pSample)
- {
- if (m_pInputPin == NULL) {
- return VFW_E_NOT_CONNECTED;
- }
- return m_pInputPin->Receive(pSample);
- }
- // called from elsewhere in our filter to pass EOS downstream to
- // our connected input pin
- HRESULT
- CBaseOutputPin::DeliverEndOfStream(void)
- {
- // remember this is on IPin not IMemInputPin
- if (m_Connected == NULL) {
- return VFW_E_NOT_CONNECTED;
- }
- return m_Connected->EndOfStream();
- }
- /* Commit the allocator's memory, this is called through IMediaFilter
- which is responsible for locking the object before calling us */
- HRESULT
- CBaseOutputPin::Active(void)
- {
- if (m_pAllocator == NULL) {
- return VFW_E_NO_ALLOCATOR;
- }
- return m_pAllocator->Commit();
- }
- /* Free up or unprepare allocator's memory, this is called through
- IMediaFilter which is responsible for locking the object first */
- HRESULT
- CBaseOutputPin::Inactive(void)
- {
- m_bRunTimeError = FALSE;
- if (m_pAllocator == NULL) {
- return VFW_E_NO_ALLOCATOR;
- }
- return m_pAllocator->Decommit();
- }
- // we have a default handling of EndOfStream which is to return
- // an error, since this should be called on input pins only
- STDMETHODIMP
- CBaseOutputPin::EndOfStream(void)
- {
- return E_UNEXPECTED;
- }
- // BeginFlush should be called on input pins only
- STDMETHODIMP
- CBaseOutputPin::BeginFlush(void)
- {
- return E_UNEXPECTED;
- }
- // EndFlush should be called on input pins only
- STDMETHODIMP
- CBaseOutputPin::EndFlush(void)
- {
- return E_UNEXPECTED;
- }
- // call BeginFlush on the connected input pin
- HRESULT
- CBaseOutputPin::DeliverBeginFlush(void)
- {
- // remember this is on IPin not IMemInputPin
- if (m_Connected == NULL) {
- return VFW_E_NOT_CONNECTED;
- }
- return m_Connected->BeginFlush();
- }
- // call EndFlush on the connected input pin
- HRESULT
- CBaseOutputPin::DeliverEndFlush(void)
- {
- // remember this is on IPin not IMemInputPin
- if (m_Connected == NULL) {
- return VFW_E_NOT_CONNECTED;
- }
- return m_Connected->EndFlush();
- }
- // deliver NewSegment to connected pin
- HRESULT
- CBaseOutputPin::DeliverNewSegment(
- REFERENCE_TIME tStart,
- REFERENCE_TIME tStop,
- double dRate)
- {
- if (m_Connected == NULL) {
- return VFW_E_NOT_CONNECTED;
- }
- return m_Connected->NewSegment(tStart, tStop, dRate);
- }
- //=====================================================================
- //=====================================================================
- // Implements CBaseInputPin
- //=====================================================================
- //=====================================================================
- /* Constructor creates a default allocator object */
- CBaseInputPin::CBaseInputPin(TCHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pPinName) :
- CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),
- m_pAllocator(NULL),
- m_bReadOnly(FALSE),
- m_bFlushing(FALSE)
- {
- ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));
- }
- #ifdef UNICODE
- CBaseInputPin::CBaseInputPin(CHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pPinName) :
- CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),
- m_pAllocator(NULL),
- m_bReadOnly(FALSE),
- m_bFlushing(FALSE)
- {
- ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));
- }
- #endif
- /* Destructor releases it's reference count on the default allocator */
- CBaseInputPin::~CBaseInputPin()
- {
- if (m_pAllocator != NULL) {
- m_pAllocator->Release();
- m_pAllocator = NULL;
- }
- }
- // override this to publicise our interfaces
- STDMETHODIMP
- CBaseInputPin::NonDelegatingQueryInterface(REFIID riid, void **ppv)
- {
- /* Do we know about this interface */
- if (riid == IID_IMemInputPin) {
- return GetInterface((IMemInputPin *) this, ppv);
- } else {
- return CBasePin::NonDelegatingQueryInterface(riid, ppv);
- }
- }
- /* Return the allocator interface that this input pin would like the output
- pin to use. NOTE subsequent calls to GetAllocator should all return an
- interface onto the SAME object so we create one object at the start
- Note:
- The allocator is Release()'d on disconnect and replaced on
- NotifyAllocator().
- Override this to provide your own allocator.
- */
- STDMETHODIMP
- CBaseInputPin::GetAllocator(
- IMemAllocator **ppAllocator)
- {
- CheckPointer(ppAllocator,E_POINTER);
- ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));
- CAutoLock cObjectLock(m_pLock);
- if (m_pAllocator == NULL) {
- HRESULT hr = CreateMemoryAllocator(&m_pAllocator);
- if (FAILED(hr)) {
- return hr;
- }
- }
- ASSERT(m_pAllocator != NULL);
- *ppAllocator = m_pAllocator;
- m_pAllocator->AddRef();
- return NOERROR;
- }
- /* Tell the input pin which allocator the output pin is actually going to use
- Override this if you care - NOTE the locking we do both here and also in
- GetAllocator is unnecessary but derived classes that do something useful
- will undoubtedly have to lock the object so this might help remind people */
- STDMETHODIMP
- CBaseInputPin::NotifyAllocator(
- IMemAllocator * pAllocator,
- BOOL bReadOnly)
- {
- CheckPointer(pAllocator,E_POINTER);
- ValidateReadPtr(pAllocator,sizeof(IMemAllocator));
- CAutoLock cObjectLock(m_pLock);
- IMemAllocator *pOldAllocator = m_pAllocator;
- pAllocator->AddRef();
- m_pAllocator = pAllocator;
- if (pOldAllocator != NULL) {
- pOldAllocator->Release();
- }
- // the readonly flag indicates whether samples from this allocator should
- // be regarded as readonly - if true, then inplace transforms will not be
- // allowed.
- m_bReadOnly = (BYTE)bReadOnly;
- return NOERROR;
- }
- HRESULT
- CBaseInputPin::BreakConnect()
- {
- /* We don't need our allocator any more */
- if (m_pAllocator) {
- // Always decommit the allocator because a downstream filter may or
- // may not decommit the connection's allocator. A memory leak could
- // occur if the allocator is not decommited when a pin is disconnected.
- HRESULT hr = m_pAllocator->Decommit();
- if( FAILED( hr ) ) {
- return hr;
- }
- m_pAllocator->Release();
- m_pAllocator = NULL;
- }
- return S_OK;
- }
- /* Do something with this media sample - this base class checks to see if the
- format has changed with this media sample and if so checks that the filter
- will accept it, generating a run time error if not. Once we have raised a
- run time error we set a flag so that no more samples will be accepted
- It is important that any filter should override this method and implement
- synchronization so that samples are not processed when the pin is
- disconnected etc
- */
- STDMETHODIMP
- CBaseInputPin::Receive(IMediaSample *pSample)
- {
- CheckPointer(pSample,E_POINTER);
- ValidateReadPtr(pSample,sizeof(IMediaSample));
- ASSERT(pSample);
- HRESULT hr = CheckStreaming();
- if (S_OK != hr) {
- return hr;
- }
- /* Check for IMediaSample2 */
- IMediaSample2 *pSample2;
- if (SUCCEEDED(pSample->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) {
- hr = pSample2->GetProperties(sizeof(m_SampleProps), (PBYTE)&m_SampleProps);
- pSample2->Release();
- if (FAILED(hr)) {
- return hr;
- }
- } else {
- /* Get the properties the hard way */
- m_SampleProps.cbData = sizeof(m_SampleProps);
- m_SampleProps.dwTypeSpecificFlags = 0;
- m_SampleProps.dwStreamId = AM_STREAM_MEDIA;
- m_SampleProps.dwSampleFlags = 0;
- if (S_OK == pSample->IsDiscontinuity()) {
- m_SampleProps.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
- }
- if (S_OK == pSample->IsPreroll()) {
- m_SampleProps.dwSampleFlags |= AM_SAMPLE_PREROLL;
- }
- if (S_OK == pSample->IsSyncPoint()) {
- m_SampleProps.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
- }
- if (SUCCEEDED(pSample->GetTime(&m_SampleProps.tStart,
- &m_SampleProps.tStop))) {
- m_SampleProps.dwSampleFlags |= AM_SAMPLE_TIMEVALID |
- AM_SAMPLE_STOPVALID;
- }
- if (S_OK == pSample->GetMediaType(&m_SampleProps.pMediaType)) {
- m_SampleProps.dwSampleFlags |= AM_SAMPLE_TYPECHANGED;
- }
- pSample->GetPointer(&m_SampleProps.pbBuffer);
- m_SampleProps.lActual = pSample->GetActualDataLength();
- m_SampleProps.cbBuffer = pSample->GetSize();
- }
- /* Has the format changed in this sample */
- if (!(m_SampleProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED)) {
- return NOERROR;
- }
- /* Check the derived class accepts this format */
- /* This shouldn't fail as the source must call QueryAccept first */
- hr = CheckMediaType((CMediaType *)m_SampleProps.pMediaType);
- if (hr == NOERROR) {
- return NOERROR;
- }
- /* Raise a runtime error if we fail the media type */
- m_bRunTimeError = TRUE;
- EndOfStream();
- m_pFilter->NotifyEvent(EC_ERRORABORT,VFW_E_TYPE_NOT_ACCEPTED,0);
- return VFW_E_INVALIDMEDIATYPE;
- }
- /* Receive multiple samples */
- STDMETHODIMP
- CBaseInputPin::ReceiveMultiple (
- IMediaSample **pSamples,
- long nSamples,
- long *nSamplesProcessed)
- {
- CheckPointer(pSamples,E_POINTER);
- ValidateReadPtr(pSamples,nSamples * sizeof(IMediaSample *));
- HRESULT hr = S_OK;
- *nSamplesProcessed = 0;
- while (nSamples-- > 0) {
- hr = Receive(pSamples[*nSamplesProcessed]);
- /* S_FALSE means don't send any more */
- if (hr != S_OK) {
- break;
- }
- (*nSamplesProcessed)++;
- }
- return hr;
- }
- /* See if Receive() might block */
- STDMETHODIMP
- CBaseInputPin::ReceiveCanBlock()
- {
- /* Ask all the output pins if they block
- If there are no output pin assume we do block
- */
- int cPins = m_pFilter->GetPinCount();
- int cOutputPins = 0;
- for (int c = 0; c < cPins; c++) {
- CBasePin *pPin = m_pFilter->GetPin(c);
- PIN_DIRECTION pd;
- HRESULT hr = pPin->QueryDirection(&pd);
- if (FAILED(hr)) {
- return hr;
- }
- if (pd == PINDIR_OUTPUT) {
- IPin *pConnected;
- hr = pPin->ConnectedTo(&pConnected);
- if (SUCCEEDED(hr)) {
- ASSERT(pConnected != NULL);
- cOutputPins++;
- IMemInputPin *pInputPin;
- hr = pConnected->QueryInterface(
- IID_IMemInputPin,
- (void **)&pInputPin);
- pConnected->Release();
- if (SUCCEEDED(hr)) {
- hr = pInputPin->ReceiveCanBlock();
- pInputPin->Release();
- if (hr != S_FALSE) {
- return S_OK;
- }
- } else {
- /* There's a transport we don't understand here */
- return S_OK;
- }
- }
- }
- }
- return cOutputPins == 0 ? S_OK : S_FALSE;
- }
- // Default handling for BeginFlush - call at the beginning
- // of your implementation (makes sure that all Receive calls
- // fail). After calling this, you need to free any queued data
- // and then call downstream.
- STDMETHODIMP
- CBaseInputPin::BeginFlush(void)
- {
- // BeginFlush is NOT synchronized with streaming but is part of
- // a control action - hence we synchronize with the filter
- CAutoLock lck(m_pLock);
- // if we are already in mid-flush, this is probably a mistake
- // though not harmful - try to pick it up for now so I can think about it
- ASSERT(!m_bFlushing);
- // first thing to do is ensure that no further Receive calls succeed
- m_bFlushing = TRUE;
- // now discard any data and call downstream - must do that
- // in derived classes
- return S_OK;
- }
- // default handling for EndFlush - call at end of your implementation
- // - before calling this, ensure that there is no queued data and no thread
- // pushing any more without a further receive, then call downstream,
- // then call this method to clear the m_bFlushing flag and re-enable
- // receives
- STDMETHODIMP
- CBaseInputPin::EndFlush(void)
- {
- // Endlush is NOT synchronized with streaming but is part of
- // a control action - hence we synchronize with the filter
- CAutoLock lck(m_pLock);
- // almost certainly a mistake if we are not in mid-flush
- ASSERT(m_bFlushing);
- // before calling, sync with pushing thread and ensure
- // no more data is going downstream, then call EndFlush on
- // downstream pins.
- // now re-enable Receives
- m_bFlushing = FALSE;
- // No more errors
- m_bRunTimeError = FALSE;
- return S_OK;
- }
- STDMETHODIMP
- CBaseInputPin::Notify(IBaseFilter * pSender, Quality q)
- {
- UNREFERENCED_PARAMETER(q);
- CheckPointer(pSender,E_POINTER);
- ValidateReadPtr(pSender,sizeof(IBaseFilter));
- DbgBreak("IQuality::Notify called on an input pin");
- return NOERROR;
- } // Notify
- /* Free up or unprepare allocator's memory, this is called through
- IMediaFilter which is responsible for locking the object first */
- HRESULT
- CBaseInputPin::Inactive(void)
- {
- m_bRunTimeError = FALSE;
- if (m_pAllocator == NULL) {
- return VFW_E_NO_ALLOCATOR;
- }
- m_bFlushing = FALSE;
- return m_pAllocator->Decommit();
- }
- // what requirements do we have of the allocator - override if you want
- // to support other people's allocators but need a specific alignment
- // or prefix.
- STDMETHODIMP
- CBaseInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES*pProps)
- {
- UNREFERENCED_PARAMETER(pProps);
- return E_NOTIMPL;
- }
- // Check if it's OK to process data
- //
- HRESULT
- CBaseInputPin::CheckStreaming()
- {
- // Shouldn't be able to get any data if we're not connected!
- ASSERT(IsConnected());
- // Don't process stuff in Stopped state
- if (IsStopped()) {
- return VFW_E_WRONG_STATE;
- }
- if (m_bFlushing) {
- return S_FALSE;
- }
- if (m_bRunTimeError) {
- return VFW_E_RUNTIME_ERROR;
- }
- return S_OK;
- }
- // Pass on the Quality notification q to
- // a. Our QualityControl sink (if we have one) or else
- // b. to our upstream filter
- // and if that doesn't work, throw it away with a bad return code
- HRESULT
- CBaseInputPin::PassNotify(Quality& q)
- {
- // We pass the message on, which means that we find the quality sink
- // for our input pin and send it there
- DbgLog((LOG_TRACE,3,TEXT("Passing Quality notification through transform")));
- if (m_pQSink!=NULL) {
- return m_pQSink->Notify(m_pFilter, q);
- } else {
- // no sink set, so pass it upstream
- HRESULT hr;
- IQualityControl * pIQC;
- hr = VFW_E_NOT_FOUND; // default
- if (m_Connected) {
- m_Connected->QueryInterface(IID_IQualityControl, (void**)&pIQC);
- if (pIQC!=NULL) {
- hr = pIQC->Notify(m_pFilter, q);
- pIQC->Release();
- }
- }
- return hr;
- }
- } // PassNotify
- //=====================================================================
- //=====================================================================
- // Memory allocation class, implements CMediaSample
- //=====================================================================
- //=====================================================================
- /* NOTE The implementation of this class calls the CUnknown constructor with
- a NULL outer unknown pointer. This has the effect of making us a self
- contained class, ie any QueryInterface, AddRef or Release calls will be
- routed to the class's NonDelegatingUnknown methods. You will typically
- find that the classes that do this then override one or more of these
- virtual functions to provide more specialised behaviour. A good example
- of this is where a class wants to keep the QueryInterface internal but
- still wants it's lifetime controlled by the external object */
- /* The last two parameters have default values of NULL and zero */
- CMediaSample::CMediaSample(TCHAR *pName,
- CBaseAllocator *pAllocator,
- HRESULT *phr,
- LPBYTE pBuffer,
- LONG length) :
- m_pBuffer(pBuffer), // Initialise the buffer
- m_cbBuffer(length), // And it's length
- m_lActual(length), // By default, actual = length
- m_pMediaType(NULL), // No media type change
- m_dwFlags(0), // Nothing set
- m_cRef(0), // 0 ref count
- m_dwTypeSpecificFlags(0), // Type specific flags
- m_dwStreamId(AM_STREAM_MEDIA), // Stream id
- m_pAllocator(pAllocator) // Allocator
- {
- /* We must have an owner and it must also be derived from class
- CBaseAllocator BUT we do not hold a reference count on it */
- ASSERT(pAllocator);
- }
- #ifdef UNICODE
- CMediaSample::CMediaSample(CHAR *pName,
- CBaseAllocator *pAllocator,
- HRESULT *phr,
- LPBYTE pBuffer,
- LONG length) :
- m_pBuffer(pBuffer), // Initialise the buffer
- m_cbBuffer(length), // And it's length
- m_lActual(length), // By default, actual = length
- m_pMediaType(NULL), // No media type change
- m_dwFlags(0), // Nothing set
- m_cRef(0), // 0 ref count
- m_dwTypeSpecificFlags(0), // Type specific flags
- m_dwStreamId(AM_STREAM_MEDIA), // Stream id
- m_pAllocator(pAllocator) // Allocator
- {
- /* We must have an owner and it must also be derived from class
- CBaseAllocator BUT we do not hold a reference count on it */
- ASSERT(pAllocator);
- }
- #endif
- /* Destructor deletes the media type memory */
- CMediaSample::~CMediaSample()
- {
- if (m_pMediaType) {
- DeleteMediaType(m_pMediaType);
- }
- }
- /* Override this to publicise our interfaces */
- STDMETHODIMP
- CMediaSample::QueryInterface(REFIID riid, void **ppv)
- {
- if (riid == IID_IMediaSample ||
- riid == IID_IMediaSample2 ||
- riid == IID_IUnknown) {
- return GetInterface((IMediaSample *) this, ppv);
- } else {
- return E_NOINTERFACE;
- }
- }
- STDMETHODIMP_(ULONG)
- CMediaSample::AddRef()
- {
- return InterlockedIncrement(&m_cRef);
- }
- // -- CMediaSample lifetimes --
- //
- // On final release of this sample buffer it is not deleted but
- // returned to the freelist of the owning memory allocator
- //
- // The allocator may be waiting for the last buffer to be placed on the free
- // list in order to decommit all the memory, so the ReleaseBuffer() call may
- // result in this sample being deleted. We also need to hold a refcount on
- // the allocator to stop that going away until we have finished with this.
- // However, we cannot release the allocator before the ReleaseBuffer, as the
- // release may cause us to be deleted. Similarly we can't do it afterwards.
- //
- // Thus we must leave it to the allocator to hold an addref on our behalf.
- // When he issues us in GetBuffer, he addref's himself. When ReleaseBuffer
- // is called, he releases himself, possibly causing us and him to be deleted.
- STDMETHODIMP_(ULONG)
- CMediaSample::Release()
- {
- /* Decrement our own private reference count */
- LONG lRef;
- if (m_cRef == 1) {
- lRef = 0;
- m_cRef = 0;
- } else {
- lRef = InterlockedDecrement(&m_cRef);
- }
- ASSERT(lRef >= 0);
- DbgLog((LOG_MEMORY,3,TEXT(" Unknown %X ref-- = %d"),
- this, m_cRef));
- /* Did we release our final reference count */
- if (lRef == 0) {
- /* Free all resources */
- if (m_dwFlags & Sample_TypeChanged) {
- SetMediaType(NULL);
- }
- ASSERT(m_pMediaType == NULL);
- m_dwFlags = 0;
- m_dwTypeSpecificFlags = 0;
- m_dwStreamId = AM_STREAM_MEDIA;
- /* This may cause us to be deleted */
- // Our refcount is reliably 0 thus no-one will mess with us
- m_pAllocator->ReleaseBuffer(this);
- }
- return (ULONG)lRef;
- }
- // set the buffer pointer and length. Used by allocators that
- // want variable sized pointers or pointers into already-read data.
- // This is only available through a CMediaSample* not an IMediaSample*
- // and so cannot be changed by clients.
- HRESULT
- CMediaSample::SetPointer(BYTE * ptr, LONG cBytes)
- {
- m_pBuffer = ptr; // new buffer area (could be null)
- m_cbBuffer = cBytes; // length of buffer
- m_lActual = cBytes; // length of data in buffer (assume full)
- return S_OK;
- }
- // get me a read/write pointer to this buffer's memory. I will actually
- // want to use sizeUsed bytes.
- STDMETHODIMP
- CMediaSample::GetPointer(BYTE ** ppBuffer)
- {
- ValidateReadWritePtr(ppBuffer,sizeof(BYTE *));
- // creator must have set pointer either during
- // constructor or by SetPointer
- ASSERT(m_pBuffer);
- *ppBuffer = m_pBuffer;
- return NOERROR;
- }
- // return the size in bytes of this buffer
- STDMETHODIMP_(LONG)
- CMediaSample::GetSize(void)
- {
- return m_cbBuffer;
- }
- // get the stream time at which this sample should start and finish.
- STDMETHODIMP
- CMediaSample::GetTime(
- REFERENCE_TIME * pTimeStart, // put time here
- REFERENCE_TIME * pTimeEnd
- )
- {
- ValidateReadWritePtr(pTimeStart,sizeof(REFERENCE_TIME));
- ValidateReadWritePtr(pTimeEnd,sizeof(REFERENCE_TIME));
- if (!(m_dwFlags & Sample_StopValid)) {
- if (!(m_dwFlags & Sample_TimeValid)) {
- return VFW_E_SAMPLE_TIME_NOT_SET;
- } else {
- *pTimeStart = m_Start;
- // Make sure old stuff works
- *pTimeEnd = m_Start + 1;
- return VFW_S_NO_STOP_TIME;
- }
- }
- *pTimeStart = m_Start;
- *pTimeEnd = m_End;
- return NOERROR;
- }
- // Set the stream time at which this sample should start and finish.
- // NULL pointers means the time is reset
- STDMETHODIMP
- CMediaSample::SetTime(
- REFERENCE_TIME * pTimeStart,
- REFERENCE_TIME * pTimeEnd
- )
- {
- if (pTimeStart == NULL) {
- ASSERT(pTimeEnd == NULL);
- m_dwFlags &= ~(Sample_TimeValid | Sample_StopValid);
- } else {
- if (pTimeEnd == NULL) {
- m_Start = *pTimeStart;
- m_dwFlags |= Sample_TimeValid;
- m_dwFlags &= ~Sample_StopValid;
- } else {
- ValidateReadPtr(pTimeStart,sizeof(REFERENCE_TIME));
- ValidateReadPtr(pTimeEnd,sizeof(REFERENCE_TIME));
- ASSERT(*pTimeEnd >= *pTimeStart);
- m_Start = *pTimeStart;
- m_End = *pTimeEnd;
- m_dwFlags |= Sample_TimeValid | Sample_StopValid;
- }
- }
- return NOERROR;
- }
- // get the media times (eg bytes) for this sample
- STDMETHODIMP
- CMediaSample::GetMediaTime(
- LONGLONG * pTimeStart,
- LONGLONG * pTimeEnd
- )
- {
- ValidateReadWritePtr(pTimeStart,sizeof(LONGLONG));
- ValidateReadWritePtr(pTimeEnd,sizeof(LONGLONG));
- if (!(m_dwFlags & Sample_MediaTimeValid)) {
- return VFW_E_MEDIA_TIME_NOT_SET;
- }
- *pTimeStart = m_MediaStart;
- *pTimeEnd = (m_MediaStart + m_MediaEnd);
- return NOERROR;
- }
- // Set the media times for this sample
- STDMETHODIMP
- CMediaSample::SetMediaTime(
- LONGLONG * pTimeStart,
- LONGLONG * pTimeEnd
- )
- {
- if (pTimeStart == NULL) {
- ASSERT(pTimeEnd == NULL);
- m_dwFlags &= ~Sample_MediaTimeValid;
- } else {
- ValidateReadPtr(pTimeStart,sizeof(LONGLONG));
- ValidateReadPtr(pTimeEnd,sizeof(LONGLONG));
- ASSERT(*pTimeEnd >= *pTimeStart);
- m_MediaStart = *pTimeStart;
- m_MediaEnd = (LONG)(*pTimeEnd - *pTimeStart);
- m_dwFlags |= Sample_MediaTimeValid;
- }
- return NOERROR;
- }
- STDMETHODIMP
- CMediaSample::IsSyncPoint(void)
- {
- if (m_dwFlags & Sample_SyncPoint) {
- return S_OK;
- } else {
- return S_FALSE;
- }
- }
- STDMETHODIMP
- CMediaSample::SetSyncPoint(BOOL bIsSyncPoint)
- {
- if (bIsSyncPoint) {
- m_dwFlags |= Sample_SyncPoint;
- } else {
- m_dwFlags &= ~Sample_SyncPoint;
- }
- return NOERROR;
- }
- // returns S_OK if there is a discontinuity in the data (this same is
- // not a continuation of the previous stream of data
- // - there has been a seek).
- STDMETHODIMP
- CMediaSample::IsDiscontinuity(void)
- {
- if (m_dwFlags & Sample_Discontinuity) {
- return S_OK;
- } else {
- return S_FALSE;
- }
- }
- // set the discontinuity property - TRUE if this sample is not a
- // continuation, but a new sample after a seek.
- STDMETHODIMP
- CMediaSample::SetDiscontinuity(BOOL bDiscont)
- {
- // should be TRUE or FALSE
- if (bDiscont) {
- m_dwFlags |= Sample_Discontinuity;
- } else {
- m_dwFlags &= ~Sample_Discontinuity;
- }
- return S_OK;
- }
- STDMETHODIMP
- CMediaSample::IsPreroll(void)
- {
- if (m_dwFlags & Sample_Preroll) {
- return S_OK;
- } else {
- return S_FALSE;
- }
- }
- STDMETHODIMP
- CMediaSample::SetPreroll(BOOL bIsPreroll)
- {
- if (bIsPreroll) {
- m_dwFlags |= Sample_Preroll;
- } else {
- m_dwFlags &= ~Sample_Preroll;
- }
- return NOERROR;
- }
- STDMETHODIMP_(LONG)
- CMediaSample::GetActualDataLength(void)
- {
- return m_lActual;
- }
- STDMETHODIMP
- CMediaSample::SetActualDataLength(LONG lActual)
- {
- if (lActual > m_cbBuffer) {
- ASSERT(lActual <= GetSize());
- return VFW_E_BUFFER_OVERFLOW;
- }
- m_lActual = lActual;
- return NOERROR;
- }
- /* These allow for limited format changes in band */
- STDMETHODIMP
- CMediaSample::GetMediaType(AM_MEDIA_TYPE **ppMediaType)
- {
- ValidateReadWritePtr(ppMediaType,sizeof(AM_MEDIA_TYPE *));
- ASSERT(ppMediaType);
- /* Do we have a new media type for them */
- if (!(m_dwFlags & Sample_TypeChanged)) {
- ASSERT(m_pMediaType == NULL);
- *ppMediaType = NULL;
- return S_FALSE;
- }
- ASSERT(m_pMediaType);
- /* Create a copy of our media type */
- *ppMediaType = CreateMediaType(m_pMediaType);
- if (*ppMediaType == NULL) {
- return E_OUTOFMEMORY;
- }
- return NOERROR;
- }
- /* Mark this sample as having a different format type */
- STDMETHODIMP
- CMediaSample::SetMediaType(AM_MEDIA_TYPE *pMediaType)
- {
- /* Delete the current media type */
- if (m_pMediaType) {
- DeleteMediaType(m_pMediaType);
- m_pMediaType = NULL;
- }
- /* Mechanism for resetting the format type */
- if (pMediaType == NULL) {
- m_dwFlags &= ~Sample_TypeChanged;
- return NOERROR;
- }
- ASSERT(pMediaType);
- ValidateReadPtr(pMediaType,sizeof(AM_MEDIA_TYPE));
- /* Take a copy of the media type */
- m_pMediaType = CreateMediaType(pMediaType);
- if (m_pMediaType == NULL) {
- m_dwFlags &= ~Sample_TypeChanged;
- return E_OUTOFMEMORY;
- }
- m_dwFlags |= Sample_TypeChanged;
- return NOERROR;
- }
- // Set and get properties (IMediaSample2)
- STDMETHODIMP CMediaSample::GetProperties(
- DWORD cbProperties,
- BYTE * pbProperties
- )
- {
- if (0 != cbProperties) {
- CheckPointer(pbProperties, E_POINTER);
- // Return generic stuff up to the length
- AM_SAMPLE2_PROPERTIES Props;
- Props.cbData = (DWORD) (min(cbProperties, sizeof(Props)));
- Props.dwSampleFlags = m_dwFlags & ~Sample_MediaTimeValid;
- Props.dwTypeSpecificFlags = m_dwTypeSpecificFlags;
- Props.pbBuffer = m_pBuffer;
- Props.cbBuffer = m_cbBuffer;
- Props.lActual = m_lActual;
- Props.tStart = m_Start;
- Props.tStop = m_End;
- Props.dwStreamId = m_dwStreamId;
- if (m_dwFlags & AM_SAMPLE_TYPECHANGED) {
- Props.pMediaType = m_pMediaType;
- } else {
- Props.pMediaType = NULL;
- }
- CopyMemory(pbProperties, &Props, Props.cbData);
- }
- return S_OK;
- }
- #define CONTAINS_FIELD(type, field, offset)
- ((FIELD_OFFSET(type, field) + sizeof(((type *)0)->field)) <= offset)
- HRESULT CMediaSample::SetProperties(
- DWORD cbProperties,
- const BYTE * pbProperties
- )
- {
- /* Generic properties */
- AM_MEDIA_TYPE *pMediaType = NULL;
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbData, cbProperties)) {
- CheckPointer(pbProperties, E_POINTER);
- AM_SAMPLE2_PROPERTIES *pProps =
- (AM_SAMPLE2_PROPERTIES *)pbProperties;
- /* Don't use more data than is actually there */
- if (pProps->cbData < cbProperties) {
- cbProperties = pProps->cbData;
- }
- /* We only handle IMediaSample2 */
- if (cbProperties > sizeof(*pProps) ||
- pProps->cbData > sizeof(*pProps)) {
- return E_INVALIDARG;
- }
- /* Do checks first, the assignments (for backout) */
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {
- /* Check the flags */
- if (pProps->dwSampleFlags &
- (~Sample_ValidFlags | Sample_MediaTimeValid)) {
- return E_INVALIDARG;
- }
- /* Check a flag isn't being set for a property
- not being provided
- */
- if ((pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) &&
- !(m_dwFlags & AM_SAMPLE_TIMEVALID) &&
- !CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {
- return E_INVALIDARG;
- }
- }
- /* NB - can't SET the pointer or size */
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pbBuffer, cbProperties)) {
- /* Check pbBuffer */
- if (pProps->pbBuffer != 0 && pProps->pbBuffer != m_pBuffer) {
- return E_INVALIDARG;
- }
- }
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties)) {
- /* Check cbBuffer */
- if (pProps->cbBuffer != 0 && pProps->cbBuffer != m_cbBuffer) {
- return E_INVALIDARG;
- }
- }
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties) &&
- CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {
- /* Check lActual */
- if (pProps->cbBuffer < pProps->lActual) {
- return E_INVALIDARG;
- }
- }
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {
- /* Check pMediaType */
- if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {
- CheckPointer(pProps->pMediaType, E_POINTER);
- pMediaType = CreateMediaType(pProps->pMediaType);
- if (pMediaType == NULL) {
- return E_OUTOFMEMORY;
- }
- }
- }
- /* Now do the assignments */
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwStreamId, cbProperties)) {
- m_dwStreamId = pProps->dwStreamId;
- }
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {
- /* Set the flags */
- m_dwFlags = pProps->dwSampleFlags |
- (m_dwFlags & Sample_MediaTimeValid);
- m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
- } else {
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwTypeSpecificFlags, cbProperties)) {
- m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
- }
- }
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {
- /* Set lActual */
- m_lActual = pProps->lActual;
- }
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {
- /* Set the times */
- m_End = pProps->tStop;
- }
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStart, cbProperties)) {
- /* Set the times */
- m_Start = pProps->tStart;
- }
- if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {
- /* Set pMediaType */
- if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {
- if (m_pMediaType != NULL) {
- DeleteMediaType(m_pMediaType);
- }
- m_pMediaType = pMediaType;
- }
- }
- /* Fix up the type changed flag to correctly reflect the current state
- If, for instance the input contained no type change but the
- output does then if we don't do this we'd lose the
- output media type.
- */
- if (m_pMediaType) {
- m_dwFlags |= Sample_TypeChanged;
- } else {
- m_dwFlags &= ~Sample_TypeChanged;
- }
- }
- return S_OK;
- }
- //
- // The streaming thread calls IPin::NewSegment(), IPin::EndOfStream(),
- // IMemInputPin::Receive() and IMemInputPin::ReceiveMultiple() on the
- // connected input pin. The application thread calls Block(). The
- // following class members can only be called by the streaming thread.
- //
- // Deliver()
- // DeliverNewSegment()
- // StartUsingOutputPin()
- // StopUsingOutputPin()
- // ChangeOutputFormat()
- // ChangeMediaType()
- // DynamicReconnect()
- //
- // The following class members can only be called by the application thread.
- //
- // Block()
- // SynchronousBlockOutputPin()
- // AsynchronousBlockOutputPin()
- //
- CDynamicOutputPin::CDynamicOutputPin(
- TCHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pName) :
- CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),
- m_hStopEvent(NULL),
- m_pGraphConfig(NULL),
- m_bPinUsesReadOnlyAllocator(FALSE),
- m_BlockState(NOT_BLOCKED),
- m_hUnblockOutputPinEvent(NULL),
- m_hNotifyCallerPinBlockedEvent(NULL),
- m_dwBlockCallerThreadID(0),
- m_dwNumOutstandingOutputPinUsers(0)
- {
- HRESULT hr = Initialize();
- if( FAILED( hr ) ) {
- *phr = hr;
- return;
- }
- }
- #ifdef UNICODE
- CDynamicOutputPin::CDynamicOutputPin(
- CHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pName) :
- CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),
- m_hStopEvent(NULL),
- m_pGraphConfig(NULL),
- m_bPinUsesReadOnlyAllocator(FALSE),
- m_BlockState(NOT_BLOCKED),
- m_hUnblockOutputPinEvent(NULL),
- m_hNotifyCallerPinBlockedEvent(NULL),
- m_dwBlockCallerThreadID(0),
- m_dwNumOutstandingOutputPinUsers(0)
- {
- HRESULT hr = Initialize();
- if( FAILED( hr ) ) {
- *phr = hr;
- return;
- }
- }
- #endif
- CDynamicOutputPin::~CDynamicOutputPin()
- {
- if(NULL != m_hUnblockOutputPinEvent) {
- // This call should not fail because we have access to m_hUnblockOutputPinEvent
- // and m_hUnblockOutputPinEvent is a valid event.
- EXECUTE_ASSERT(::CloseHandle(m_hUnblockOutputPinEvent));
- }
- if(NULL != m_hNotifyCallerPinBlockedEvent) {
- // This call should not fail because we have access to m_hNotifyCallerPinBlockedEvent
- // and m_hNotifyCallerPinBlockedEvent is a valid event.
- EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
- }
- }
- HRESULT CDynamicOutputPin::Initialize(void)
- {
- m_hUnblockOutputPinEvent = ::CreateEvent( NULL, // The event will have the default security descriptor.
- TRUE, // This is a manual reset event.
- TRUE, // The event is initially signaled.
- NULL ); // The event is not named.
- // CreateEvent() returns NULL if an error occurs.
- if(NULL == m_hUnblockOutputPinEvent) {
- return AmGetLastErrorToHResult();
- }
- // Set flag to say we can reconnect while streaming.
- SetReconnectWhenActive(true);
- return S_OK;
- }
- STDMETHODIMP CDynamicOutputPin::NonDelegatingQueryInterface(REFIID riid, void **ppv)
- {
- if(riid == IID_IPinFlowControl) {
- return GetInterface(static_cast<IPinFlowControl*>(this), ppv);
- } else {
- return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
- }
- }
- STDMETHODIMP CDynamicOutputPin::Disconnect(void)
- {
- CAutoLock cObjectLock(m_pLock);
- return DisconnectInternal();
- }
- STDMETHODIMP CDynamicOutputPin::Block(DWORD dwBlockFlags, HANDLE hEvent)
- {
- const DWORD VALID_FLAGS = AM_PIN_FLOW_CONTROL_BLOCK;
- // Check for illegal flags.
- if(dwBlockFlags & ~VALID_FLAGS) {
- return E_INVALIDARG;
- }
- // Make sure the event is unsignaled.
- if((dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) && (NULL != hEvent)) {
- if( !::ResetEvent( hEvent ) ) {
- return AmGetLastErrorToHResult();
- }
- }
- // No flags are set if we are unblocking the output pin.
- if(0 == dwBlockFlags) {
- // This parameter should be NULL because unblock operations are always synchronous.
- // There is no need to notify the caller when the event is done.
- if(NULL != hEvent) {
- return E_INVALIDARG;
- }
- }
- #ifdef DEBUG
- AssertValid();
- #endif // DEBUG
- HRESULT hr;
- if(dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) {
- // IPinFlowControl::Block()'s hEvent parameter is NULL if the block is synchronous.
- // If hEvent is not NULL, the block is asynchronous.
- if(NULL == hEvent) {
- hr = SynchronousBlockOutputPin();
- } else {
- hr = AsynchronousBlockOutputPin(hEvent);
- }
- } else {
- hr = UnblockOutputPin();
- }
- #ifdef DEBUG
- AssertValid();
- #endif // DEBUG
- if(FAILED(hr)) {
- return hr;
- }
- return S_OK;
- }
- HRESULT CDynamicOutputPin::SynchronousBlockOutputPin(void)
- {
- HANDLE hNotifyCallerPinBlockedEvent = :: CreateEvent( NULL, // The event will have the default security attributes.
- FALSE, // This is an automatic reset event.
- FALSE, // The event is initially unsignaled.
- NULL ); // The event is not named.
- // CreateEvent() returns NULL if an error occurs.
- if(NULL == hNotifyCallerPinBlockedEvent) {
- return AmGetLastErrorToHResult();
- }
- HRESULT hr = AsynchronousBlockOutputPin(hNotifyCallerPinBlockedEvent);
- if(FAILED(hr)) {
- // This call should not fail because we have access to hNotifyCallerPinBlockedEvent
- // and hNotifyCallerPinBlockedEvent is a valid event.
- EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));
- return hr;
- }
- hr = WaitEvent(hNotifyCallerPinBlockedEvent);
- // This call should not fail because we have access to hNotifyCallerPinBlockedEvent
- // and hNotifyCallerPinBlockedEvent is a valid event.
- EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));
- if(FAILED(hr)) {
- return hr;
- }
- return S_OK;
- }
- HRESULT CDynamicOutputPin::AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent)
- {
- // This function holds the m_BlockStateLock because it uses
- // m_dwBlockCallerThreadID, m_BlockState and
- // m_hNotifyCallerPinBlockedEvent.
- CAutoLock alBlockStateLock(&m_BlockStateLock);
- if(NOT_BLOCKED != m_BlockState) {
- if(m_dwBlockCallerThreadID == ::GetCurrentThreadId()) {
- return VFW_E_PIN_ALREADY_BLOCKED_ON_THIS_THREAD;
- } else {
- return VFW_E_PIN_ALREADY_BLOCKED;
- }
- }
- BOOL fSuccess = ::DuplicateHandle( ::GetCurrentProcess(),
- hNotifyCallerPinBlockedEvent,
- ::GetCurrentProcess(),
- &m_hNotifyCallerPinBlockedEvent,
- EVENT_MODIFY_STATE,
- FALSE,
- 0 );
- if( !fSuccess ) {
- return AmGetLastErrorToHResult();
- }
- m_BlockState = PENDING;
- m_dwBlockCallerThreadID = ::GetCurrentThreadId();
- // The output pin cannot be blocked if the streaming thread is
- // calling IPin::NewSegment(), IPin::EndOfStream(), IMemInputPin::Receive()
- // or IMemInputPin::ReceiveMultiple() on the connected input pin. Also, it
- // cannot be blocked if the streaming thread is calling DynamicReconnect(),
- // ChangeMediaType() or ChangeOutputFormat().
- if(!StreamingThreadUsingOutputPin()) {
- // The output pin can be immediately blocked.
- BlockOutputPin();
- }
- return S_OK;
- }
- void CDynamicOutputPin::BlockOutputPin(void)
- {
- // The caller should always hold the m_BlockStateLock because this function
- // uses m_BlockState and m_hNotifyCallerPinBlockedEvent.
- ASSERT(CritCheckIn(&m_BlockStateLock));
- // This function should not be called if the streaming thread is modifying
- // the connection state or it's passing data downstream.
- ASSERT(!StreamingThreadUsingOutputPin());
- // This should not fail because we successfully created the event
- // and we have the security permissions to change it's state.
- EXECUTE_ASSERT(::ResetEvent(m_hUnblockOutputPinEvent));
- // This event should not fail because AsynchronousBlockOutputPin() successfully
- // duplicated this handle and we have the appropriate security permissions.
- EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));
- EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
- m_BlockState = BLOCKED;
- m_hNotifyCallerPinBlockedEvent = NULL;
- }
- HRESULT CDynamicOutputPin::UnblockOutputPin(void)
- {
- // UnblockOutputPin() holds the m_BlockStateLock because it
- // uses m_BlockState, m_dwBlockCallerThreadID and
- // m_hNotifyCallerPinBlockedEvent.
- CAutoLock alBlockStateLock(&m_BlockStateLock);
- if(NOT_BLOCKED == m_BlockState) {
- return S_FALSE;
- }
- // This should not fail because we successfully created the event
- // and we have the security permissions to change it's state.
- EXECUTE_ASSERT(::SetEvent(m_hUnblockOutputPinEvent));
- // Cancel the block operation if it's still pending.
- if(NULL != m_hNotifyCallerPinBlockedEvent) {
- // This event should not fail because AsynchronousBlockOutputPin() successfully
- // duplicated this handle and we have the appropriate security permissions.
- EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));
- EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
- }
- m_BlockState = NOT_BLOCKED;
- m_dwBlockCallerThreadID = 0;
- m_hNotifyCallerPinBlockedEvent = NULL;
- return S_OK;
- }
- HRESULT CDynamicOutputPin::StartUsingOutputPin(void)
- {
- // The caller should not hold m_BlockStateLock. If the caller does,
- // a deadlock could occur.
- ASSERT(CritCheckOut(&m_BlockStateLock));
- CAutoLock alBlockStateLock(&m_BlockStateLock);
- #ifdef DEBUG
- AssertValid();
- #endif // DEBUG
- // Are we in the middle of a block operation?
- while(BLOCKED == m_BlockState) {
- m_BlockStateLock.Unlock();
- // If this ASSERT fires, a deadlock could occur. The caller should make sure
- // that this thread never acquires the Block State lock more than once.
- ASSERT(CritCheckOut( &m_BlockStateLock ));
- // WaitForMultipleObjects() returns WAIT_OBJECT_0 if the unblock event
- // is fired. It returns WAIT_OBJECT_0 + 1 if the stop event if fired.
- // See the Windows SDK documentation for more information on
- // WaitForMultipleObjects().
- const DWORD UNBLOCK = WAIT_OBJECT_0;
- const DWORD STOP = WAIT_OBJECT_0 + 1;
- HANDLE ahWaitEvents[] = { m_hUnblockOutputPinEvent, m_hStopEvent };
- DWORD dwNumWaitEvents = sizeof(ahWaitEvents)/sizeof(HANDLE);
- DWORD dwReturnValue = ::WaitForMultipleObjects( dwNumWaitEvents, ahWaitEvents, FALSE, INFINITE );
- m_BlockStateLock.Lock();
- #ifdef DEBUG
- AssertValid();
- #endif // DEBUG
- switch( dwReturnValue ) {
- case UNBLOCK:
- break;
- case STOP:
- return VFW_E_STATE_CHANGED;
- case WAIT_FAILED:
- return AmGetLastErrorToHResult();
- default:
- DbgBreak( "An Unexpected case occured in CDynamicOutputPin::StartUsingOutputPin()." );
- return E_UNEXPECTED;
- }
- }
- m_dwNumOutstandingOutputPinUsers++;
- #ifdef DEBUG
- AssertValid();
- #endif // DEBUG
- return S_OK;
- }
- void CDynamicOutputPin::StopUsingOutputPin(void)
- {
- CAutoLock alBlockStateLock(&m_BlockStateLock);
- #ifdef DEBUG
- AssertValid();
- #endif // DEBUG
- m_dwNumOutstandingOutputPinUsers--;
- if((m_dwNumOutstandingOutputPinUsers == 0) && (NOT_BLOCKED != m_BlockState)) {
- BlockOutputPin();
- }
- #ifdef DEBUG
- AssertValid();
- #endif // DEBUG
- }
- bool CDynamicOutputPin::StreamingThreadUsingOutputPin(void)
- {
- CAutoLock alBlockStateLock(&m_BlockStateLock);
- return (m_dwNumOutstandingOutputPinUsers > 0);
- }
- void CDynamicOutputPin::SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent)
- {
- // This pointer is not addrefed because filters are not allowed to
- // hold references to the filter graph manager. See the documentation for
- // IBaseFilter::JoinFilterGraph() in the Direct Show SDK for more information.
- m_pGraphConfig = pGraphConfig;
- m_hStopEvent = hStopEvent;
- }
- HRESULT CDynamicOutputPin::Active(void)
- {
- // Make sure the user initialized the object by calling SetConfigInfo().
- if((NULL == m_hStopEvent) || (NULL == m_pGraphConfig)) {
- DbgBreak( ERROR: CDynamicOutputPin::Active() failed because m_pGraphConfig and m_hStopEvent were not initialized. Call SetConfigInfo() to initialize them. );
- return E_FAIL;
- }
- // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
- // The ASSERT can also fire if the event if destroyed and then Active() is called. An event
- // handle is invalid if 1) the event does not exist or the user does not have the security
- // permissions to use the event.
- EXECUTE_ASSERT(ResetEvent(m_hStopEvent));
- return CBaseOutputPin::Active();
- }
- HRESULT CDynamicOutputPin::Inactive(void)
- {
- // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
- // The ASSERT can also fire if the event if destroyed and then Active() is called. An event
- // handle is invalid if 1) the event does not exist or the user does not have the security
- // permissions to use the event.
- EXECUTE_ASSERT(SetEvent(m_hStopEvent));
- return CBaseOutputPin::Inactive();
- }
- HRESULT CDynamicOutputPin::DeliverBeginFlush(void)
- {
- // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
- // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.
- // An event handle is invalid if 1) the event does not exist or the user does not have the security
- // permissions to use the event.
- EXECUTE_ASSERT(SetEvent(m_hStopEvent));
- return CBaseOutputPin::DeliverBeginFlush();
- }
- HRESULT CDynamicOutputPin::DeliverEndFlush(void)
- {
- // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
- // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.
- // An event handle is invalid if 1) the event does not exist or the user does not have the security
- // permissions to use the event.
- EXECUTE_ASSERT(ResetEvent(m_hStopEvent));
- return CBaseOutputPin::DeliverEndFlush();
- }
- // ChangeOutputFormat() either dynamicly changes the connection's format type or it dynamicly
- // reconnects the output pin.
- HRESULT CDynamicOutputPin::ChangeOutputFormat
- (
- const AM_MEDIA_TYPE *pmt,
- REFERENCE_TIME tSegmentStart,
- REFERENCE_TIME tSegmentStop,
- double dSegmentRate
- )
- {
- // The caller should call StartUsingOutputPin() before calling this
- // method.
- ASSERT(StreamingThreadUsingOutputPin());
- // Callers should always pass a valid media type to ChangeOutputFormat() .
- ASSERT(NULL != pmt);
- CMediaType cmt(*pmt);
- HRESULT hr = ChangeMediaType(&cmt);
- if (FAILED(hr)) {
- return hr;
- }
- hr = DeliverNewSegment(tSegmentStart, tSegmentStop, dSegmentRate);
- if( FAILED( hr ) ) {
- return hr;
- }
- return S_OK;
- }
- HRESULT CDynamicOutputPin::ChangeMediaType(const CMediaType *pmt)
- {
- // The caller should call StartUsingOutputPin() before calling this
- // method.
- ASSERT(StreamingThreadUsingOutputPin());
- // This function assumes the filter graph is running.
- ASSERT(!IsStopped());
- if(!IsConnected()) {
- return VFW_E_NOT_CONNECTED;
- }
- /* First check if the downstream pin will accept a dynamic
- format change
- */
- QzCComPtr<IPinConnection> pConnection;
- m_Connected->QueryInterface(IID_IPinConnection, (void **)&pConnection);
- if(pConnection != NULL) {
- if(S_OK == pConnection->DynamicQueryAccept(pmt)) {
- HRESULT hr = ChangeMediaTypeHelper(pmt);
- if(FAILED(hr)) {
- return hr;
- }
- return S_OK;
- }
- }
- /* Can't do the dynamic connection */
- return DynamicReconnect(pmt);
- }
- HRESULT CDynamicOutputPin::ChangeMediaTypeHelper(const CMediaType *pmt)
- {
- // The caller should call StartUsingOutputPin() before calling this
- // method.
- ASSERT(StreamingThreadUsingOutputPin());
- HRESULT hr = m_Connected->ReceiveConnection(this, pmt);
- if(FAILED(hr)) {
- return hr;
- }
- hr = SetMediaType(pmt);
- if(FAILED(hr)) {
- return hr;
- }
- // Does this pin use the local memory transport?
- if(NULL != m_pInputPin) {
- // This function assumes that m_pInputPin and m_Connected are
- // two different interfaces to the same object.
- ASSERT(::IsEqualObject(m_Connected, m_pInputPin));
- ALLOCATOR_PROPERTIES apInputPinRequirements;
- apInputPinRequirements.cbAlign = 0;
- apInputPinRequirements.cbBuffer = 0;
- apInputPinRequirements.cbPrefix = 0;
- apInputPinRequirements.cBuffers = 0;
- m_pInputPin->GetAllocatorRequirements(&apInputPinRequirements);
- // A zero allignment does not make any sense.
- if(0 == apInputPinRequirements.cbAlign) {
- apInputPinRequirements.cbAlign = 1;
- }
- hr = m_pAllocator->Decommit();
- if(FAILED(hr)) {
- return hr;
- }
- hr = DecideBufferSize(m_pAllocator, &apInputPinRequirements);
- if(FAILED(hr)) {
- return hr;
- }
- hr = m_pAllocator->Commit();
- if(FAILED(hr)) {
- return hr;
- }
- hr = m_pInputPin->NotifyAllocator(m_pAllocator, m_bPinUsesReadOnlyAllocator);
- if(FAILED(hr)) {
- return hr;
- }
- }
- return S_OK;
- }
- // this method has to be called from the thread that is pushing data,
- // and it's the caller's responsibility to make sure that the thread
- // has no outstand samples because they cannot be delivered after a
- // reconnect
- //
- HRESULT CDynamicOutputPin::DynamicReconnect( const CMediaType* pmt )
- {
- // The caller should call StartUsingOutputPin() before calling this
- // method.
- ASSERT(StreamingThreadUsingOutputPin());
- if((m_pGraphConfig == NULL) || (NULL == m_hStopEvent)) {
- return E_FAIL;
- }
- HRESULT hr = m_pGraphConfig->Reconnect(
- this,
- NULL,
- pmt,
- NULL,
- m_hStopEvent,
- AM_GRAPH_CONFIG_RECONNECT_CACHE_REMOVED_FILTERS );
- return hr;
- }
- HRESULT CDynamicOutputPin::CompleteConnect(IPin *pReceivePin)
- {
- HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);
- if(SUCCEEDED(hr)) {
- if(!IsStopped() && m_pAllocator) {
- hr = m_pAllocator->Commit();
- ASSERT(hr != VFW_E_ALREADY_COMMITTED);
- }
- }
- return hr;
- }
- #ifdef DEBUG
- void CDynamicOutputPin::AssertValid(void)
- {
- // Make sure the object was correctly initialized.
- // This ASSERT only fires if the object failed to initialize
- // and the user ignored the constructor's return code (phr).
- ASSERT(NULL != m_hUnblockOutputPinEvent);
- // If either of these ASSERTs fire, the user did not correctly call
- // SetConfigInfo().
- ASSERT(NULL != m_hStopEvent);
- ASSERT(NULL != m_pGraphConfig);
- // Make sure the block state is consistent.
- CAutoLock alBlockStateLock(&m_BlockStateLock);
- // BLOCK_STATE variables only have three legal values: PENDING, BLOCKED and NOT_BLOCKED.
- ASSERT((NOT_BLOCKED == m_BlockState) || (PENDING == m_BlockState) || (BLOCKED == m_BlockState));
- // m_hNotifyCallerPinBlockedEvent is only needed when a block operation cannot complete
- // immediately.
- ASSERT(((NULL == m_hNotifyCallerPinBlockedEvent) && (PENDING != m_BlockState)) ||
- ((NULL != m_hNotifyCallerPinBlockedEvent) && (PENDING == m_BlockState)) );
- // m_dwBlockCallerThreadID should always be 0 if the pin is not blocked and
- // the user is not trying to block the pin.
- ASSERT((0 == m_dwBlockCallerThreadID) || (NOT_BLOCKED != m_BlockState));
- // If this ASSERT fires, the streaming thread is using the output pin and the
- // output pin is blocked.
- ASSERT(((0 != m_dwNumOutstandingOutputPinUsers) && (BLOCKED != m_BlockState)) ||
- ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED != m_BlockState)) ||
- ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED == m_BlockState)) );
- }
- #endif // DEBUG
- HRESULT CDynamicOutputPin::WaitEvent(HANDLE hEvent)
- {
- const DWORD EVENT_SIGNALED = WAIT_OBJECT_0;
- DWORD dwReturnValue = ::WaitForSingleObject(hEvent, INFINITE);
- switch( dwReturnValue ) {
- case EVENT_SIGNALED:
- return S_OK;
- case WAIT_FAILED:
- return AmGetLastErrorToHResult();
- default:
- DbgBreak( "An Unexpected case occured in CDynamicOutputPin::WaitEvent()." );
- return E_UNEXPECTED;
- }
- }
- //=====================================================================
- //=====================================================================
- // Implements CBaseAllocator
- //=====================================================================
- //=====================================================================
- /* Constructor overrides the default settings for the free list to request
- that it be alertable (ie the list can be cast to a handle which can be
- passed to WaitForSingleObject). Both of the allocator lists also ask for
- object locking, the all list matches the object default settings but I
- have included them here just so it is obvious what kind of list it is */
- CBaseAllocator::CBaseAllocator(TCHAR *pName,
- LPUNKNOWN pUnk,
- HRESULT *phr,
- BOOL bEvent,
- BOOL fEnableReleaseCallback
- ) :
- CUnknown(pName, pUnk),
- m_lAllocated(0),
- m_bChanged(FALSE),
- m_bCommitted(FALSE),
- m_bDecommitInProgress(FALSE),
- m_lSize(0),
- m_lCount(0),
- m_lAlignment(0),
- m_lPrefix(0),
- m_hSem(NULL),
- m_lWaiting(0),
- m_fEnableReleaseCallback(fEnableReleaseCallback),
- m_pNotify(NULL)
- {
- if (bEvent) {
- m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
- if (m_hSem == NULL) {
- *phr = E_OUTOFMEMORY;
- return;
- }
- }
- }
- #ifdef UNICODE
- CBaseAllocator::CBaseAllocator(CHAR *pName,
- LPUNKNOWN pUnk,
- HRESULT *phr,
- BOOL bEvent,
- BOOL fEnableReleaseCallback) :
- CUnknown(pName, pUnk),
- m_lAllocated(0),
- m_bChanged(FALSE),
- m_bCommitted(FALSE),
- m_bDecommitInProgress(FALSE),
- m_lSize(0),
- m_lCount(0),
- m_lAlignment(0),
- m_lPrefix(0),
- m_hSem(NULL),
- m_lWaiting(0),
- m_fEnableReleaseCallback(fEnableReleaseCallback),
- m_pNotify(NULL)
- {
- if (bEvent) {
- m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
- if (m_hSem == NULL) {
- *phr = E_OUTOFMEMORY;
- return;
- }
- }
- }
- #endif
- /* Destructor */
- CBaseAllocator::~CBaseAllocator()
- {
- // we can't call Decommit here since that would mean a call to a
- // pure virtual in destructor.
- // We must assume that the derived class has gone into decommit state in
- // its destructor.
- ASSERT(!m_bCommitted);
- if (m_hSem != NULL) {
- EXECUTE_ASSERT(CloseHandle(m_hSem));
- }
- if (m_pNotify) {
- m_pNotify->Release();
- }
- }
- /* Override this to publicise our interfaces */
- STDMETHODIMP
- CBaseAllocator::NonDelegatingQueryInterface(REFIID riid, void **ppv)
- {
- /* Do we know about this interface */
- if (riid == IID_IMemAllocator ||
- riid == IID_IMemAllocatorCallbackTemp && m_fEnableReleaseCallback) {
- return GetInterface((IMemAllocatorCallbackTemp *) this, ppv);
- } else {
- return CUnknown::NonDelegatingQueryInterface(riid, ppv);
- }
- }
- /* This sets the size and count of the required samples. The memory isn't
- actually allocated until Commit() is called, if memory has already been
- allocated then assuming no samples are outstanding the user may call us
- to change the buffering, the memory will be released in Commit() */
- STDMETHODIMP
- CBaseAllocator::SetProperties(
- ALLOCATOR_PROPERTIES* pRequest,
- ALLOCATOR_PROPERTIES* pActual)
- {
- CheckPointer(pRequest, E_POINTER);
- CheckPointer(pActual, E_POINTER);
- ValidateReadWritePtr(pActual, sizeof(ALLOCATOR_PROPERTIES));
- CAutoLock cObjectLock(this);
- ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));
- ASSERT(pRequest->cbBuffer > 0);
- /* Check the alignment requested */
- if (pRequest->cbAlign != 1) {
- DbgLog((LOG_ERROR, 2, TEXT("Alignment requested was 0x%x, not 1"),
- pRequest->cbAlign));
- return VFW_E_BADALIGN;
- }
- /* Can't do this if already committed, there is an argument that says we
- should not reject the SetProperties call if there are buffers still
- active. However this is called by the source filter, which is the same
- person who is holding the samples. Therefore it is not unreasonable
- for them to free all their samples before changing the requirements */
- if (m_bCommitted) {
- return VFW_E_ALREADY_COMMITTED;
- }
- /* Must be no outstanding buffers */
- if (m_lAllocated != m_lFree.GetCount()) {
- return VFW_E_BUFFERS_OUTSTANDING;
- }
- /* There isn't any real need to check the parameters as they
- will just be rejected when the user finally calls Commit */
- pActual->cbBuffer = m_lSize = pRequest->cbBuffer;
- pActual->cBuffers = m_lCount = pRequest->cBuffers;
- pActual->cbAlign = m_lAlignment = pRequest->cbAlign;
- pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;
- m_bChanged = TRUE;
- return NOERROR;
- }
- STDMETHODIMP
- CBaseAllocator::GetProperties(
- ALLOCATOR_PROPERTIES * pActual)
- {
- CheckPointer(pActual,E_POINTER);
- ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));
- CAutoLock cObjectLock(this);
- pActual->cbBuffer = m_lSize;
- pActual->cBuffers = m_lCount;
- pActual->cbAlign = m_lAlignment;
- pActual->cbPrefix = m_lPrefix;
- return NOERROR;
- }
- // get container for a sample. Blocking, synchronous call to get the
- // next free buffer (as represented by an IMediaSample interface).
- // on return, the time etc properties will be invalid, but the buffer
- // pointer and size will be correct.
- HRESULT CBaseAllocator::GetBuffer(IMediaSample **ppBuffer,
- REFERENCE_TIME *pStartTime,
- REFERENCE_TIME *pEndTime,
- DWORD dwFlags
- )
- {
- UNREFERENCED_PARAMETER(pStartTime);
- UNREFERENCED_PARAMETER(pEndTime);
- UNREFERENCED_PARAMETER(dwFlags);
- CMediaSample *pSample;
- *ppBuffer = NULL;
- for (;;)
- {
- { // scope for lock
- CAutoLock cObjectLock(this);
- /* Check we are committed */
- if (!m_bCommitted) {
- return VFW_E_NOT_COMMITTED;
- }
- pSample = (CMediaSample *) m_lFree.RemoveHead();
- if (pSample == NULL) {
- SetWaiting();
- }
- }
- /* If we didn't get a sample then wait for the list to signal */
- if (pSample) {
- break;
- }
- if (dwFlags & AM_GBF_NOWAIT) {
- return VFW_E_TIMEOUT;
- }
- ASSERT(m_hSem != NULL);
- WaitForSingleObject(m_hSem, INFINITE);
- }
- /* Addref the buffer up to one. On release
- back to zero instead of being deleted, it will requeue itself by
- calling the ReleaseBuffer member function. NOTE the owner of a
- media sample must always be derived from CBaseAllocator */
- ASSERT(pSample->m_cRef == 0);
- pSample->m_cRef = 1;
- *ppBuffer = pSample;
- return NOERROR;
- }
- /* Final release of a CMediaSample will call this */
- STDMETHODIMP
- CBaseAllocator::ReleaseBuffer(IMediaSample * pSample)
- {
- CheckPointer(pSample,E_POINTER);
- ValidateReadPtr(pSample,sizeof(IMediaSample));
- BOOL bRelease = FALSE;
- {
- CAutoLock cal(this);
- /* Put back on the free list */
- m_lFree.Add((CMediaSample *)pSample);
- if (m_lWaiting != 0) {
- NotifySample();
- }
- // if there is a pending Decommit, then we need to complete it by
- // calling Free() when the last buffer is placed on the free list
- LONG l1 = m_lFree.GetCount();
- if (m_bDecommitInProgress && (l1 == m_lAllocated)) {
- Free();
- m_bDecommitInProgress = FALSE;
- bRelease = TRUE;
- }
- }
- if (m_pNotify) {
- ASSERT(m_fEnableReleaseCallback);
- //
- // Note that this is not synchronized with setting up a notification
- // method.
- //
- m_pNotify->NotifyRelease();
- }
- /* For each buffer there is one AddRef, made in GetBuffer and released
- here. This may cause the allocator and all samples to be deleted */
- if (bRelease) {
- Release();
- }
- return NOERROR;
- }
- STDMETHODIMP
- CBaseAllocator::SetNotify(
- IMemAllocatorNotifyCallbackTemp* pNotify
- )
- {
- ASSERT(m_fEnableReleaseCallback);
- CAutoLock lck(this);
- if (pNotify) {
- pNotify->AddRef();
- }
- if (m_pNotify) {
- m_pNotify->Release();
- }
- m_pNotify = pNotify;
- return S_OK;
- }
- STDMETHODIMP
- CBaseAllocator::GetFreeCount(
- LONG* plBuffersFree
- )
- {
- ASSERT(m_fEnableReleaseCallback);
- CAutoLock cObjectLock(this);
- *plBuffersFree = m_lCount - m_lAllocated + m_lFree.GetCount();
- return NOERROR;
- }
- void
- CBaseAllocator::NotifySample()
- {
- if (m_lWaiting != 0) {
- ASSERT(m_hSem != NULL);
- ReleaseSemaphore(m_hSem, m_lWaiting, 0);
- m_lWaiting = 0;
- }
- }
- STDMETHODIMP
- CBaseAllocator::Commit()
- {
- /* Check we are not decommitted */
- CAutoLock cObjectLock(this);
- // cannot need to alloc or re-alloc if we are committed
- if (m_bCommitted) {
- return NOERROR;
- }
- /* Allow GetBuffer calls */
- m_bCommitted = TRUE;
- // is there a pending decommit ? if so, just cancel it
- if (m_bDecommitInProgress) {
- m_bDecommitInProgress = FALSE;
- // don't call Alloc at this point. He cannot allow SetProperties
- // between Decommit and the last free, so the buffer size cannot have
- // changed. And because some of the buffers are not free yet, he
- // cannot re-alloc anyway.
- return NOERROR;
- }
- DbgLog((LOG_MEMORY, 1, TEXT("Allocating: %ldx%ld"), m_lCount, m_lSize));
- // actually need to allocate the samples
- HRESULT hr = Alloc();
- if (FAILED(hr)) {
- m_bCommitted = FALSE;
- return hr;
- }
- AddRef();
- return NOERROR;
- }
- STDMETHODIMP
- CBaseAllocator::Decommit()
- {
- BOOL bRelease = FALSE;
- {
- /* Check we are not already decommitted */
- CAutoLock cObjectLock(this);
- if (m_bCommitted == FALSE) {
- if (m_bDecommitInProgress == FALSE) {
- return NOERROR;
- }
- }
- /* No more GetBuffer calls will succeed */
- m_bCommitted = FALSE;
- // are any buffers outstanding?
- if (m_lFree.GetCount() < m_lAllocated) {
- // please complete the decommit when last buffer is freed
- m_bDecommitInProgress = TRUE;
- } else {
- m_bDecommitInProgress = FALSE;
- // need to complete the decommit here as there are no
- // outstanding buffers
- Free();
- bRelease = TRUE;
- }
- // Tell anyone waiting that they can go now so we can
- // reject their call
- NotifySample();
- }
- if (bRelease) {
- Release();
- }
- return NOERROR;
- }
- /* Base definition of allocation which checks we are ok to go ahead and do
- the full allocation. We return S_FALSE if the requirements are the same */
- HRESULT
- CBaseAllocator::Alloc(void)
- {
- /* Error if he hasn't set the size yet */
- if (m_lCount <= 0 || m_lSize <= 0 || m_lAlignment <= 0) {
- return VFW_E_SIZENOTSET;
- }
- /* should never get here while buffers outstanding */
- ASSERT(m_lFree.GetCount() == m_lAllocated);
- /* If the requirements haven't changed then don't reallocate */
- if (m_bChanged == FALSE) {
- return S_FALSE;
- }
- return NOERROR;
- }
- /* Implement CBaseAllocator::CSampleList::Remove(pSample)
- Removes pSample from the list
- */
- void
- CBaseAllocator::CSampleList::Remove(CMediaSample * pSample)
- {
- CMediaSample **pSearch;
- for (pSearch = &m_List;
- *pSearch != NULL;
- pSearch = &(CBaseAllocator::NextSample(*pSearch))) {
- if (*pSearch == pSample) {
- *pSearch = CBaseAllocator::NextSample(pSample);
- CBaseAllocator::NextSample(pSample) = NULL;
- m_nOnList--;
- return;
- }
- }
- DbgBreak("Couldn't find sample in list");
- }
- //=====================================================================
- //=====================================================================
- // Implements CMemAllocator
- //=====================================================================
- //=====================================================================
- /* This goes in the factory template table to create new instances */
- CUnknown *CMemAllocator::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
- {
- CUnknown *pUnkRet = new CMemAllocator(NAME("CMemAllocator"), pUnk, phr);
- return pUnkRet;
- }
- CMemAllocator::CMemAllocator(
- TCHAR *pName,
- LPUNKNOWN pUnk,
- HRESULT *phr)
- : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),
- m_pBuffer(NULL)
- {
- }
- #ifdef UNICODE
- CMemAllocator::CMemAllocator(
- CHAR *pName,
- LPUNKNOWN pUnk,
- HRESULT *phr)
- : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),
- m_pBuffer(NULL)
- {
- }
- #endif
- /* This sets the size and count of the required samples. The memory isn't
- actually allocated until Commit() is called, if memory has already been
- allocated then assuming no samples are outstanding the user may call us
- to change the buffering, the memory will be released in Commit() */
- STDMETHODIMP
- CMemAllocator::SetProperties(
- ALLOCATOR_PROPERTIES* pRequest,
- ALLOCATOR_PROPERTIES* pActual)
- {
- CheckPointer(pActual,E_POINTER);
- ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));
- CAutoLock cObjectLock(this);
- ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));
- ASSERT(pRequest->cbBuffer > 0);
- SYSTEM_INFO SysInfo;
- GetSystemInfo(&SysInfo);
- /* Check the alignment request is a power of 2 */
- if ((-pRequest->cbAlign & pRequest->cbAlign) != pRequest->cbAlign) {
- DbgLog((LOG_ERROR, 1, TEXT("Alignment requested 0x%x not a power of 2!"),
- pRequest->cbAlign));
- }
- /* Check the alignment requested */
- if (pRequest->cbAlign == 0 ||
- (SysInfo.dwAllocationGranularity & (pRequest->cbAlign - 1)) != 0) {
- DbgLog((LOG_ERROR, 1, TEXT("Invalid alignment 0x%x requested - granularity = 0x%x"),
- pRequest->cbAlign, SysInfo.dwAllocationGranularity));
- return VFW_E_BADALIGN;
- }
- /* Can't do this if already committed, there is an argument that says we
- should not reject the SetProperties call if there are buffers still
- active. However this is called by the source filter, which is the same
- person who is holding the samples. Therefore it is not unreasonable
- for them to free all their samples before changing the requirements */
- if (m_bCommitted == TRUE) {
- return VFW_E_ALREADY_COMMITTED;
- }
- /* Must be no outstanding buffers */
- if (m_lFree.GetCount() < m_lAllocated) {
- return VFW_E_BUFFERS_OUTSTANDING;
- }
- /* There isn't any real need to check the parameters as they
- will just be rejected when the user finally calls Commit */
- // round length up to alignment - remember that prefix is included in
- // the alignment
- LONG lSize = pRequest->cbBuffer + pRequest->cbPrefix;
- LONG lRemainder = lSize % pRequest->cbAlign;
- if (lRemainder != 0) {
- lSize = lSize - lRemainder + pRequest->cbAlign;
- }
- pActual->cbBuffer = m_lSize = (lSize - pRequest->cbPrefix);
- pActual->cBuffers = m_lCount = pRequest->cBuffers;
- pActual->cbAlign = m_lAlignment = pRequest->cbAlign;
- pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;
- m_bChanged = TRUE;
- return NOERROR;
- }
- // override this to allocate our resources when Commit is called.
- //
- // note that our resources may be already allocated when this is called,
- // since we don't free them on Decommit. We will only be called when in
- // decommit state with all buffers free.
- //
- // object locked by caller
- HRESULT
- CMemAllocator::Alloc(void)
- {
- CAutoLock lck(this);
- /* Check he has called SetProperties */
- HRESULT hr = CBaseAllocator::Alloc();
- if (FAILED(hr)) {
- return hr;
- }
- /* If the requirements haven't changed then don't reallocate */
- if (hr == S_FALSE) {
- ASSERT(m_pBuffer);
- return NOERROR;
- }
- ASSERT(hr == S_OK); // we use this fact in the loop below
- /* Free the old resources */
- if (m_pBuffer) {
- ReallyFree();
- }
- /* Compute the aligned size */
- LONG lAlignedSize = m_lSize + m_lPrefix;
- if (m_lAlignment > 1) {
- LONG lRemainder = lAlignedSize % m_lAlignment;
- if (lRemainder != 0) {
- lAlignedSize += (m_lAlignment - lRemainder);
- }
- }
- /* Create the contiguous memory block for the samples
- making sure it's properly aligned (64K should be enough!)
- */
- ASSERT(lAlignedSize % m_lAlignment == 0);
- m_pBuffer = (PBYTE)VirtualAlloc(NULL,
- m_lCount * lAlignedSize,
- MEM_COMMIT,
- PAGE_READWRITE);
- if (m_pBuffer == NULL) {
- return E_OUTOFMEMORY;
- }
- LPBYTE pNext = m_pBuffer;
- CMediaSample *pSample;
- ASSERT(m_lAllocated == 0);
- // Create the new samples - we have allocated m_lSize bytes for each sample
- // plus m_lPrefix bytes per sample as a prefix. We set the pointer to
- // the memory after the prefix - so that GetPointer() will return a pointer
- // to m_lSize bytes.
- for (; m_lAllocated < m_lCount; m_lAllocated++, pNext += lAlignedSize) {
- pSample = new CMediaSample(
- NAME("Default memory media sample"),
- this,
- &hr,
- pNext + m_lPrefix, // GetPointer() value
- m_lSize); // not including prefix
- ASSERT(SUCCEEDED(hr));
- if (pSample == NULL) {
- return E_OUTOFMEMORY;
- }
- // This CANNOT fail
- m_lFree.Add(pSample);
- }
- m_bChanged = FALSE;
- return NOERROR;
- }
- // override this to free up any resources we have allocated.
- // called from the base class on Decommit when all buffers have been
- // returned to the free list.
- //
- // caller has already locked the object.
- // in our case, we keep the memory until we are deleted, so
- // we do nothing here. The memory is deleted in the destructor by
- // calling ReallyFree()
- void
- CMemAllocator::Free(void)
- {
- return;
- }
- // called from the destructor (and from Alloc if changing size/count) to
- // actually free up the memory
- void
- CMemAllocator::ReallyFree(void)
- {
- /* Should never be deleting this unless all buffers are freed */
- ASSERT(m_lAllocated == m_lFree.GetCount());
- /* Free up all the CMediaSamples */
- CMediaSample *pSample;
- for (;;) {
- pSample = m_lFree.RemoveHead();
- if (pSample != NULL) {
- delete pSample;
- } else {
- break;
- }
- }
- m_lAllocated = 0;
- // free the block of buffer memory
- if (m_pBuffer) {
- EXECUTE_ASSERT(VirtualFree(m_pBuffer, 0, MEM_RELEASE));
- m_pBuffer = NULL;
- }
- }
- /* Destructor frees our memory resources */
- CMemAllocator::~CMemAllocator()
- {
- Decommit();
- ReallyFree();
- }
- // ------------------------------------------------------------------------
- // filter registration through IFilterMapper. used if IFilterMapper is
- // not found (Quartz 1.0 install)
- STDAPI
- AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata
- , IFilterMapper * pIFM
- , BOOL bRegister )
- {
- DbgLog((LOG_TRACE, 3, TEXT("= AMovieSetupRegisterFilter")));
- // check we've got data
- //
- if( NULL == psetupdata ) return S_FALSE;
- // unregister filter
- // (as pins are subkeys of filter's CLSID key
- // they do not need to be removed separately).
- //
- DbgLog((LOG_TRACE, 3, TEXT("= = unregister filter")));
- HRESULT hr = pIFM->UnregisterFilter( *(psetupdata->clsID) );
- if( bRegister )
- {
- // register filter
- //
- DbgLog((LOG_TRACE, 3, TEXT("= = register filter")));
- hr = pIFM->RegisterFilter( *(psetupdata->clsID)
- , psetupdata->strName
- , psetupdata->dwMerit );
- if( SUCCEEDED(hr) )
- {
- // all its pins
- //
- DbgLog((LOG_TRACE, 3, TEXT("= = register filter pins")));
- for( UINT m1=0; m1 < psetupdata->nPins; m1++ )
- {
- hr = pIFM->RegisterPin( *(psetupdata->clsID)
- , psetupdata->lpPin[m1].strName
- , psetupdata->lpPin[m1].bRendered
- , psetupdata->lpPin[m1].bOutput
- , psetupdata->lpPin[m1].bZero
- , psetupdata->lpPin[m1].bMany
- , *(psetupdata->lpPin[m1].clsConnectsToFilter)
- , psetupdata->lpPin[m1].strConnectsToPin );
- if( SUCCEEDED(hr) )
- {
- // and each pin's media types
- //
- DbgLog((LOG_TRACE, 3, TEXT("= = register filter pin types")));
- for( UINT m2=0; m2 < psetupdata->lpPin[m1].nMediaTypes; m2++ )
- {
- hr = pIFM->RegisterPinType( *(psetupdata->clsID)
- , psetupdata->lpPin[m1].strName
- , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMajorType)
- , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMinorType) );
- if( FAILED(hr) ) break;
- }
- if( FAILED(hr) ) break;
- }
- if( FAILED(hr) ) break;
- }
- }
- }
- // handle one acceptable "error" - that
- // of filter not being registered!
- // (couldn't find a suitable #define'd
- // name for the error!)
- //
- if( 0x80070002 == hr)
- return NOERROR;
- else
- return hr;
- }
- // Remove warnings about unreferenced inline functions
- #pragma warning(disable:4514)