📄 transfrm.cpp
字号:
//------------------------------------------------------------------------------
// File: Transfrm.cpp
//
// Desc: DirectShow base classes - implements class for simple transform
// filters such as video decompressors.
//
// Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
#include <streams.h>
#include <measure.h>
// =================================================================
// Implements the CTransformFilter class
// =================================================================
CTransformFilter::CTransformFilter(TCHAR *pName,
LPUNKNOWN pUnk,
REFCLSID clsid) :
CBaseFilter(pName,pUnk,&m_csFilter, clsid),
m_pInput(NULL),
m_pOutput(NULL),
m_bEOSDelivered(FALSE),
m_bQualityChanged(FALSE),
m_bSampleSkipped(FALSE)
{
#ifdef PERF
RegisterPerfId();
#endif // PERF
}
#ifdef UNICODE
CTransformFilter::CTransformFilter(char *pName,
LPUNKNOWN pUnk,
REFCLSID clsid) :
CBaseFilter(pName,pUnk,&m_csFilter, clsid),
m_pInput(NULL),
m_pOutput(NULL),
m_bEOSDelivered(FALSE),
m_bQualityChanged(FALSE),
m_bSampleSkipped(FALSE)
{
#ifdef PERF
RegisterPerfId();
#endif // PERF
}
#endif
// destructor
CTransformFilter::~CTransformFilter()
{
// Delete the pins
delete m_pInput;
delete m_pOutput;
}
// Transform place holder - should never be called
HRESULT CTransformFilter::Transform(IMediaSample * pIn, IMediaSample *pOut)
{
UNREFERENCED_PARAMETER(pIn);
UNREFERENCED_PARAMETER(pOut);
DbgBreak("CTransformFilter::Transform() should never be called");
return E_UNEXPECTED;
}
// return the number of pins we provide
int CTransformFilter::GetPinCount()
{
return 2;
}
// return a non-addrefed CBasePin * for the user to addref if he holds onto it
// for longer than his pointer to us. We create the pins dynamically when they
// are asked for rather than in the constructor. This is because we want to
// give the derived class an oppportunity to return different pin objects
// We return the objects as and when they are needed. If either of these fails
// then we return NULL, the assumption being that the caller will realise the
// whole deal is off and destroy us - which in turn will delete everything.
CBasePin *
CTransformFilter::GetPin(int n)
{
HRESULT hr = S_OK;
// Create an input pin if necessary
if (m_pInput == NULL) {
m_pInput = new CTransformInputPin(NAME("Transform input pin"),
this, // Owner filter
&hr, // Result code
L"XForm In"); // Pin name
// Can't fail
ASSERT(SUCCEEDED(hr));
if (m_pInput == NULL) {
return NULL;
}
m_pOutput = (CTransformOutputPin *)
new CTransformOutputPin(NAME("Transform output pin"),
this, // Owner filter
&hr, // Result code
L"XForm Out"); // Pin name
// Can't fail
ASSERT(SUCCEEDED(hr));
if (m_pOutput == NULL) {
delete m_pInput;
m_pInput = NULL;
}
}
// Return the appropriate pin
if (n == 0) {
return m_pInput;
} else
if (n == 1) {
return m_pOutput;
} else {
return NULL;
}
}
//
// FindPin
//
// If Id is In or Out then return the IPin* for that pin
// creating the pin if need be. Otherwise return NULL with an error.
STDMETHODIMP CTransformFilter::FindPin(LPCWSTR Id, IPin **ppPin)
{
CheckPointer(ppPin,E_POINTER);
ValidateReadWritePtr(ppPin,sizeof(IPin *));
if (0==lstrcmpW(Id,L"In")) {
*ppPin = GetPin(0);
} else if (0==lstrcmpW(Id,L"Out")) {
*ppPin = GetPin(1);
} else {
*ppPin = NULL;
return VFW_E_NOT_FOUND;
}
HRESULT hr = NOERROR;
// AddRef() returned pointer - but GetPin could fail if memory is low.
if (*ppPin) {
(*ppPin)->AddRef();
} else {
hr = E_OUTOFMEMORY; // probably. There's no pin anyway.
}
return hr;
}
// override these two functions if you want to inform something
// about entry to or exit from streaming state.
HRESULT
CTransformFilter::StartStreaming()
{
return NOERROR;
}
HRESULT
CTransformFilter::StopStreaming()
{
return NOERROR;
}
// override this to grab extra interfaces on connection
HRESULT
CTransformFilter::CheckConnect(PIN_DIRECTION dir,IPin *pPin)
{
UNREFERENCED_PARAMETER(dir);
UNREFERENCED_PARAMETER(pPin);
return NOERROR;
}
// place holder to allow derived classes to release any extra interfaces
HRESULT
CTransformFilter::BreakConnect(PIN_DIRECTION dir)
{
UNREFERENCED_PARAMETER(dir);
return NOERROR;
}
// Let derived classes know about connection completion
HRESULT
CTransformFilter::CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin)
{
UNREFERENCED_PARAMETER(direction);
UNREFERENCED_PARAMETER(pReceivePin);
return NOERROR;
}
// override this to know when the media type is really set
HRESULT
CTransformFilter::SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt)
{
UNREFERENCED_PARAMETER(direction);
UNREFERENCED_PARAMETER(pmt);
return NOERROR;
}
// Set up our output sample
HRESULT
CTransformFilter::InitializeOutputSample(IMediaSample *pSample, IMediaSample **ppOutSample)
{
IMediaSample *pOutSample;
// default - times are the same
AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
DWORD dwFlags = m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0;
// This will prevent the image renderer from switching us to DirectDraw
// when we can't do it without skipping frames because we're not on a
// keyframe. If it really has to switch us, it still will, but then we
// will have to wait for the next keyframe
if (!(pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT)) {
dwFlags |= AM_GBF_NOTASYNCPOINT;
}
ASSERT(m_pOutput->m_pAllocator != NULL);
HRESULT hr = m_pOutput->m_pAllocator->GetBuffer(
&pOutSample
, pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID ?
&pProps->tStart : NULL
, pProps->dwSampleFlags & AM_SAMPLE_STOPVALID ?
&pProps->tStop : NULL
, dwFlags
);
*ppOutSample = pOutSample;
if (FAILED(hr)) {
return hr;
}
ASSERT(pOutSample);
IMediaSample2 *pOutSample2;
if (SUCCEEDED(pOutSample->QueryInterface(IID_IMediaSample2,
(void **)&pOutSample2))) {
/* Modify it */
AM_SAMPLE2_PROPERTIES OutProps;
EXECUTE_ASSERT(SUCCEEDED(pOutSample2->GetProperties(
FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, tStart), (PBYTE)&OutProps)
));
OutProps.dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
OutProps.dwSampleFlags =
(OutProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED) |
(pProps->dwSampleFlags & ~AM_SAMPLE_TYPECHANGED);
OutProps.tStart = pProps->tStart;
OutProps.tStop = pProps->tStop;
OutProps.cbData = FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId);
hr = pOutSample2->SetProperties(
FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId),
(PBYTE)&OutProps
);
if (pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {
m_bSampleSkipped = FALSE;
}
pOutSample2->Release();
} else {
if (pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) {
pOutSample->SetTime(&pProps->tStart,
&pProps->tStop);
}
if (pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT) {
pOutSample->SetSyncPoint(TRUE);
}
if (pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {
pOutSample->SetDiscontinuity(TRUE);
m_bSampleSkipped = FALSE;
}
// Copy the media times
LONGLONG MediaStart, MediaEnd;
if (pSample->GetMediaTime(&MediaStart,&MediaEnd) == NOERROR) {
pOutSample->SetMediaTime(&MediaStart,&MediaEnd);
}
}
return S_OK;
}
// override this to customize the transform process
HRESULT
CTransformFilter::Receive(IMediaSample *pSample)
{
/* Check for other streams and pass them on */
AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
if (pProps->dwStreamId != AM_STREAM_MEDIA) {
return m_pOutput->m_pInputPin->Receive(pSample);
}
HRESULT hr;
ASSERT(pSample);
IMediaSample * pOutSample;
// If no output to deliver to then no point sending us data
ASSERT (m_pOutput != NULL) ;
// Set up the output sample
hr = InitializeOutputSample(pSample, &pOutSample);
if (FAILED(hr)) {
return hr;
}
// Start timing the transform (if PERF is defined)
MSR_START(m_idTransform);
// have the derived class transform the data
hr = Transform(pSample, pOutSample);
// Stop the clock and log it (if PERF is defined)
MSR_STOP(m_idTransform);
if (FAILED(hr)) {
DbgLog((LOG_TRACE,1,TEXT("Error from transform")));
} else {
// the Transform() function can return S_FALSE to indicate that the
// sample should not be delivered; we only deliver the sample if it's
// really S_OK (same as NOERROR, of course.)
if (hr == NOERROR) {
hr = m_pOutput->m_pInputPin->Receive(pOutSample);
m_bSampleSkipped = FALSE; // last thing no longer dropped
} else {
// S_FALSE returned from Transform is a PRIVATE agreement
// We should return NOERROR from Receive() in this cause because returning S_FALSE
// from Receive() means that this is the end of the stream and no more data should
// be sent.
if (S_FALSE == hr) {
// Release the sample before calling notify to avoid
// deadlocks if the sample holds a lock on the system
// such as DirectDraw buffers do
pOutSample->Release();
m_bSampleSkipped = TRUE;
if (!m_bQualityChanged) {
NotifyEvent(EC_QUALITY_CHANGE,0,0);
m_bQualityChanged = TRUE;
}
return NOERROR;
}
}
}
// release the output buffer. If the connected pin still needs it,
// it will have addrefed it itself.
pOutSample->Release();
return hr;
}
// Return S_FALSE to mean "pass the note on upstream"
// Return NOERROR (Same as S_OK)
// to mean "I've done something about it, don't pass it on"
HRESULT CTransformFilter::AlterQuality(Quality q)
{
UNREFERENCED_PARAMETER(q);
return S_FALSE;
}
// EndOfStream received. Default behaviour is to deliver straight
// downstream, since we have no queued data. If you overrode Receive
// and have queue data, then you need to handle this and deliver EOS after
// all queued data is sent
HRESULT
CTransformFilter::EndOfStream(void)
{
HRESULT hr = NOERROR;
if (m_pOutput != NULL) {
hr = m_pOutput->DeliverEndOfStream();
}
return hr;
}
// enter flush state. Receives already blocked
// must override this if you have queued data or a worker thread
HRESULT
CTransformFilter::BeginFlush(void)
{
HRESULT hr = NOERROR;
if (m_pOutput != NULL) {
// block receives -- done by caller (CBaseInputPin::BeginFlush)
// discard queued data -- we have no queued data
// free anyone blocked on receive - not possible in this filter
// call downstream
hr = m_pOutput->DeliverBeginFlush();
}
return hr;
}
// leave flush state. must override this if you have queued data
// or a worker thread
HRESULT
CTransformFilter::EndFlush(void)
{
// sync with pushing thread -- we have no worker thread
// ensure no more data to go downstream -- we have no queued data
// call EndFlush on downstream pins
ASSERT (m_pOutput != NULL);
return m_pOutput->DeliverEndFlush();
// caller (the input pin's method) will unblock Receives
}
// override these so that the derived filter can catch them
STDMETHODIMP
CTransformFilter::Stop()
{
CAutoLock lck1(&m_csFilter);
if (m_State == State_Stopped) {
return NOERROR;
}
// Succeed the Stop if we are not completely connected
ASSERT(m_pInput == NULL || m_pOutput != NULL);
if (m_pInput == NULL || m_pInput->IsConnected() == FALSE ||
m_pOutput->IsConnected() == FALSE) {
m_State = State_Stopped;
m_bEOSDelivered = FALSE;
return NOERROR;
}
ASSERT(m_pInput);
ASSERT(m_pOutput);
// decommit the input pin before locking or we can deadlock
m_pInput->Inactive();
// synchronize with Receive calls
CAutoLock lck2(&m_csReceive);
m_pOutput->Inactive();
// allow a class derived from CTransformFilter
// to know about starting and stopping streaming
HRESULT hr = StopStreaming();
if (SUCCEEDED(hr)) {
// complete the state transition
m_State = State_Stopped;
m_bEOSDelivered = FALSE;
}
return hr;
}
STDMETHODIMP
CTransformFilter::Pause()
{
CAutoLock lck(&m_csFilter);
HRESULT hr = NOERROR;
if (m_State == State_Paused) {
// (This space left deliberately blank)
}
// If we have no input pin or it isn't yet connected then when we are
// asked to pause we deliver an end of stream to the downstream filter.
// This makes sure that it doesn't sit there forever waiting for
// samples which we cannot ever deliver without an input connection.
else if (m_pInput == NULL || m_pInput->IsConnected() == FALSE) {
if (m_pOutput && m_bEOSDelivered == FALSE) {
m_pOutput->DeliverEndOfStream();
m_bEOSDelivered = TRUE;
}
m_State = State_Paused;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -