
前面在讲解媒体类型协商的过程中提到过CheckMediaType这个函数,当时只是说明了这个函数是用来检查输入的媒体类型是否符合要求,今天我们就来重点了解一下这个函数。
我们首先看一下CBasePin类中关于CheckMediaType函数的声明:
class AM_NOVTABLE CBasePin : public CUnknown, public IPin, public IQualityControl
{
protected:
// used to agree a media type for a pin connection
// given a specific media type, attempt a connection (includes
// checking that the type is acceptable to this pin)
HRESULT
AttemptConnection(
IPin* pReceivePin, // connect to this pin
const CMediaType* pmt // using this type
);
// try all the media types in this enumerator - for each that
// we accept, try to connect using ReceiveConnection.
HRESULT TryMediaTypes(
IPin *pReceivePin, // connect to this pin
__in_opt const CMediaType *pmt, // proposed type from Connect
IEnumMediaTypes *pEnum); // try this enumerator
// establish a connection with a suitable mediatype. Needs to
// propose a media type if the pmt pointer is null or partially
// specified - use TryMediaTypes on both our and then the other pin's
// enumerator until we find one that works.
HRESULT AgreeMediaType(
IPin *pReceivePin, // connect to this pin
const CMediaType *pmt); // proposed type from Connect
public:
CBasePin(
__in_opt LPCTSTR pObjectName, // Object description
__in CBaseFilter *pFilter, // Owning filter who knows about pins
__in CCritSec *pLock, // Object who implements the lock
__inout HRESULT *phr, // General OLE return code
__in_opt LPCWSTR pName, // Pin name for us
PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT
#ifdef UNICODE
CBasePin(
__in_opt LPCSTR pObjectName, // Object description
__in CBaseFilter *pFilter, // Owning filter who knows about pins
__in CCritSec *pLock, // Object who implements the lock
__inout HRESULT *phr, // General OLE return code
__in_opt LPCWSTR pName, // Pin name for us
PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT
#endif
virtual ~CBasePin();
//省略部分接口
// check if the pin can support this specific proposed type and format
virtual HRESULT CheckMediaType(const CMediaType *) PURE;
//省略部分接口
};
由于CBasePin类的声明比较长,所以我删除了大部分代码,只留下了CheckMediaType函数的声明。从声明我们可以看出,这个函数是纯虚函数,没有具体实现,那这个函数在哪里实现的呢?
我们带着这个疑问再来看看CBasePin的几个派生类的源码:
CBaseOutputPin类声明:
class AM_NOVTABLE CBaseOutputPin : public CBasePin
{
protected:
IMemAllocator *m_pAllocator;
IMemInputPin *m_pInputPin; // interface on the downstreaminput pin
// set up in CheckConnect when we connect.
public:
CBaseOutputPin(
__in_opt LPCTSTR pObjectName,
__in CBaseFilter *pFilter,
__in CCritSec *pLock,
__inout HRESULT *phr,
__in_opt LPCWSTR pName);
#ifdef UNICODE
CBaseOutputPin(
__in_opt LPCSTR pObjectName,
__in CBaseFilter *pFilter,
__in CCritSec *pLock,
__inout HRESULT *phr,
__in_opt LPCWSTR pName);
#endif
// override CompleteConnect() so we can negotiate an allocator
virtual HRESULT CompleteConnect(IPin *pReceivePin);
// negotiate the allocator and its buffer size/count and other properties
// Calls DecideBufferSize to set properties
virtual HRESULT DecideAllocator(IMemInputPin * pPin, __deref_out IMemAllocator ** pAlloc);
// override this to set the buffer size and count. Return an error
// if the size/count is not to your liking.
// The allocator properties passed in are those requested by the
// input pin - use eg the alignment and prefix members if you have
// no preference on these.
virtual HRESULT DecideBufferSize(
IMemAllocator * pAlloc,
__inout ALLOCATOR_PROPERTIES * ppropInputRequest
) PURE;
// returns an empty sample buffer from the allocator
virtual HRESULT GetDeliveryBuffer(__deref_out IMediaSample ** ppSample,
__in_opt REFERENCE_TIME * pStartTime,
__in_opt REFERENCE_TIME * pEndTime,
DWORD dwFlags);
// deliver a filled-in sample to the connected input pin
// note - you need to release it after calling this. The receiving
// pin will addref the sample if it needs to hold it beyond the
// call.
virtual HRESULT Deliver(IMediaSample *);
// override this to control the connection
virtual HRESULT InitAllocator(__deref_out IMemAllocator **ppAlloc);
HRESULT CheckConnect(IPin *pPin);
HRESULT BreakConnect();
// override to call Commit and Decommit
HRESULT Active(void);
HRESULT Inactive(void);
// we have a default handling of EndOfStream which is to return
// an error, since this should be called on input pins only
STDMETHODIMP EndOfStream(void);
// called from elsewhere in our filter to pass EOS downstream to
// our connected input pin
virtual HRESULT DeliverEndOfStream(void);
// same for Begin/EndFlush - we handle Begin/EndFlush since it
// is an error on an output pin, and we have Deliver methods to
// call the methods on the connected pin
STDMETHODIMP BeginFlush(void);
STDMETHODIMP EndFlush(void);
virtual HRESULT DeliverBeginFlush(void);
virtual HRESULT DeliverEndFlush(void);
// deliver NewSegment to connected pin - you will need to
// override this if you queue any data in your output pin.
virtual HRESULT DeliverNewSegment(
REFERENCE_TIME tStart,
REFERENCE_TIME tStop,
double dRate);
//================================================================================
// IQualityControl methods
//================================================================================
// All inherited from CBasePin and not overridden here.
// STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
// STDMETHODIMP SetSink(IQualityControl * piqc);
};
从类声明可知CBaseOutputPin没有对CheckMediaType函数进行重写。需要从CBaseOutputPin派生的其他类实现这个函数。
我们再看看CSourceStream类的声明(此类从CBaseOutputPin派生):
class CSourceStream : public CAMThread, public CBaseOutputPin {
public:
CSourceStream(__in_opt LPCTSTR pObjectName,
__inout HRESULT *phr,
__inout CSource *pms,
__in_opt LPCWSTR pName);
#ifdef UNICODE
CSourceStream(__in_opt LPCSTR pObjectName,
__inout HRESULT *phr,
__inout CSource *pms,
__in_opt LPCWSTR pName);
#endif
virtual ~CSourceStream(void); // virtual destructor ensures derived class destructors are called too.
protected:
CSource *m_pFilter; // The parent of this stream
// *
// * Data Source
// *
// * The following three functions: FillBuffer, OnThreadCreate/Destroy, are
// * called from within the ThreadProc. They are used in the creation of
// * the media samples this pin will provide
// *
// Override this to provide the worker thread a means
// of processing a buffer
virtual HRESULT FillBuffer(IMediaSample *pSamp) PURE;
// Called as the thread is created/destroyed - use to perform
// jobs such as start/stop streaming mode
// If OnThreadCreate returns an error the thread will exit.
virtual HRESULT OnThreadCreate(void) {return NOERROR;};
virtual HRESULT OnThreadDestroy(void) {return NOERROR;};
virtual HRESULT OnThreadStartPlay(void) {return NOERROR;};
// *
// * Worker Thread
// *
HRESULT Active(void); // Starts up the worker thread
HRESULT Inactive(void); // Exits the worker thread.
public:
// thread commands
enum Command {CMD_INIT, CMD_PAUSE, CMD_RUN, CMD_STOP, CMD_EXIT};
HRESULT Init(void) { return CallWorker(CMD_INIT); }
HRESULT Exit(void) { return CallWorker(CMD_EXIT); }
HRESULT Run(void) { return CallWorker(CMD_RUN); }
HRESULT Pause(void) { return CallWorker(CMD_PAUSE); }
HRESULT Stop(void) { return CallWorker(CMD_STOP); }
protected:
Command GetRequest(void) { return (Command) CAMThread::GetRequest(); }
BOOL CheckRequest(Command *pCom) { return CAMThread::CheckRequest( (DWORD *) pCom); }
// override these if you want to add thread commands
virtual DWORD ThreadProc(void); // the thread function
virtual HRESULT DoBufferProcessingLoop(void); // the loop executed whilst running
// *
// * AM_MEDIA_TYPE support
// *
// If you support more than one media type then override these 2 functions
virtual HRESULT CheckMediaType(const CMediaType *pMediaType);
virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType); // List pos. 0-n
// If you support only one type then override this fn.
// This will only be called by the default implementations
// of CheckMediaType and GetMediaType(int, CMediaType*)
// You must override this fn. or the above 2!
virtual HRESULT GetMediaType(__inout CMediaType *pMediaType) {return E_UNEXPECTED;}
STDMETHODIMP QueryId(
__deref_out LPWSTR * Id
);
};
我们可以看到此类才真正实现了CheckMediaType函数。
我们再来看看此类中这个函数的具体实现代码:
// Do we support this type? Provides the default support for 1 type.
HRESULT CSourceStream::CheckMediaType(const CMediaType *pMediaType) {
CAutoLock lock(m_pFilter->pStateLock());
CMediaType mt;
GetMediaType(&mt);
if (mt == *pMediaType) {
return NOERROR;
}
return E_FAIL;
}
实现比较简单,就是取到此类默认支持的媒体类型,然后和传入的媒体类型比较,如果相同就返回成功,否则返回失败。
前面我们看了输出PIN关于CheckMediaType函数的实现情况,我们接下来看看关于输入PIN的。
CBaseInputPin类的声明:
class AM_NOVTABLE CBaseInputPin : public CBasePin,
public IMemInputPin
{
protected:
IMemAllocator *m_pAllocator; // Default memory allocator
// allocator is read-only, so received samples
// cannot be modified (probably only relevant to in-place
// transforms
BYTE m_bReadOnly;
// in flushing state (between BeginFlush and EndFlush)
// if TRUE, all Receives are returned with S_FALSE
BYTE m_bFlushing;
// Sample properties - initalized in Receive
AM_SAMPLE2_PROPERTIES m_SampleProps;
public:
CBaseInputPin(
__in_opt LPCTSTR pObjectName,
__in CBaseFilter *pFilter,
__in CCritSec *pLock,
__inout HRESULT *phr,
__in_opt LPCWSTR pName);
#ifdef UNICODE
CBaseInputPin(
__in_opt LPCSTR pObjectName,
__in CBaseFilter *pFilter,
__in CCritSec *pLock,
__inout HRESULT *phr,
__in_opt LPCWSTR pName);
#endif
virtual ~CBaseInputPin();
DECLARE_IUNKNOWN
// override this to publicise our interfaces
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
// return the allocator interface that this input pin
// would like the output pin to use
STDMETHODIMP GetAllocator(__deref_out IMemAllocator ** ppAllocator);
// tell the input pin which allocator the output pin is actually
// going to use.
STDMETHODIMP NotifyAllocator(
IMemAllocator * pAllocator,
BOOL bReadOnly);
// do something with this media sample
STDMETHODIMP Receive(IMediaSample *pSample);
// do something with these media samples
STDMETHODIMP ReceiveMultiple (
__in_ecount(nSamples) IMediaSample **pSamples,
long nSamples,
__out long *nSamplesProcessed);
// See if Receive() blocks
STDMETHODIMP ReceiveCanBlock();
// 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 BeginFlush(void);
// 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 EndFlush(void);
// this method is optional (can return E_NOTIMPL).
// default implementation returns E_NOTIMPL. Override if you have
// specific alignment or prefix needs, but could use an upstream
// allocator
STDMETHODIMP GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps);
// Release the pin's allocator.
HRESULT BreakConnect();
// helper method to check the read-only flag
BOOL IsReadOnly() {
return m_bReadOnly;
};
// helper method to see if we are flushing
BOOL IsFlushing() {
return m_bFlushing;
};
// Override this for checking whether it's OK to process samples
// Also call this from EndOfStream.
virtual HRESULT CheckStreaming();
// Pass a Quality notification on to the appropriate sink
HRESULT PassNotify(Quality& q);
//================================================================================
// IQualityControl methods (from CBasePin)
//================================================================================
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
// no need to override:
// STDMETHODIMP SetSink(IQualityControl * piqc);
// switch the pin to inactive state - may already be inactive
virtual HRESULT Inactive(void);
// Return sample properties pointer
AM_SAMPLE2_PROPERTIES * SampleProps() {
ASSERT(m_SampleProps.cbData != 0);
return &m_SampleProps;
}
};
从声明了解到这个类也没有实现CheckMediaType函数,我们只能继续从派生类中查找了。
CRendererInputPin类的声明(继承自CBaseInputPin类):
class CRendererInputPin : public CBaseInputPin
{
protected:
CBaseRenderer *m_pRenderer;
public:
CRendererInputPin(__inout CBaseRenderer *pRenderer,
__inout HRESULT *phr,
__in_opt LPCWSTR Name);
// Overriden from the base pin classes
HRESULT BreakConnect();
HRESULT CompleteConnect(IPin *pReceivePin);
HRESULT SetMediaType(const CMediaType *pmt);
HRESULT CheckMediaType(const CMediaType *pmt);
HRESULT Active();
HRESULT Inactive();
// Add rendering behaviour to interface functions
STDMETHODIMP QueryId(__deref_out LPWSTR *Id);
STDMETHODIMP EndOfStream();
STDMETHODIMP BeginFlush();
STDMETHODIMP EndFlush();
STDMETHODIMP Receive(IMediaSample *pMediaSample);
// Helper
IMemAllocator inline *Allocator() const
{
return m_pAllocator;
}
};
可以看到此类有对CheckMediaType的实现,我们再来看看具体的实现代码:
// Will the filter accept this media type
HRESULT CRendererInputPin::CheckMediaType(const CMediaType *pmt)
{
return m_pRenderer->CheckMediaType(pmt);
}
从实现可知它将CheckMeidaType函数转交给了Filter对象。所以具体的媒体类型检查是到Filter当中。
我们再来看看CMpcVideoRenderer这个Filter中关于此函数的实现情况:
CMpcVideoRenderer类的继承情况如下:
CMpcVideoRenderer-->CBaseVideoRenderer2-->CBaseRenderer-->CBaseFilter.
CheckMeidaType这个函数就是从CBaseRenderer类开始声明的。只是这个类里定义的函数为纯虚函数。
CBaseRenderer类声明如下:
class CBaseRenderer : public CBaseFilter
{
protected:
friend class CRendererInputPin;
//省略部分代码
// Derived classes MUST override these
virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE;
virtual HRESULT CheckMediaType(const CMediaType *) PURE;
// Helper
void WaitForReceiveToComplete();
};
我看了CBaseVideoRenderer2类的声明,没有对CheckMeidaType的实现,所以具体的实现就交给了CMpcVideoRenderer类对象。
CMpcVideoRenderer类声明如下:
class __declspec(uuid("71F080AA-8661-4093-B15E-4F6903E77D0A"))
CMpcVideoRenderer
: public CBaseVideoRenderer2
, public IKsPropertySet
, public IMFGetService
, public IBasicVideo2
, public IVideoWindow
, public ISpecifyPropertyPages
, public IVideoRenderer
, public ISubRender
, public CExFilterConfigImpl
, public ID3DFullscreenControl
{
private:
public:
CMpcVideoRenderer(LPUNKNOWN pUnk, HRESULT* phr);
~CMpcVideoRenderer();
void NewSegment(REFERENCE_TIME startTime);
long CalcImageSize(CMediaType& mt, bool redefine_mt);
// CBaseRenderer
HRESULT CheckMediaType(const CMediaType *pmt) override;
HRESULT SetMediaType(const CMediaType *pmt) override;
HRESULT DoRenderSample(IMediaSample* pSample) override;
HRESULT Receive(IMediaSample* pMediaSample) override;
HRESULT BeginFlush() override;
HRESULT EndFlush() override;
void UpdateDisplayInfo();
void OnDisplayModeChange(const bool bReset = false);
void OnWindowMove();
DECLARE_IUNKNOWN
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
// IMediaFilter
STDMETHODIMP Run(REFERENCE_TIME rtStart) override;
STDMETHODIMP Pause() override;
STDMETHODIMP Stop() override;
// IKsPropertySet
// IMFGetService
// IDispatch
// IBasicVideo
// IBasicVideo2
// IVideoWindow
// ISpecifyPropertyPages
// IVideoRenderer
// ISubRender
// IExFilterConfig
// ID3DFullscreenControl
private:
};
从类声明可知,此类实现了CheckMeidaType函数,我们再来看看具体的实现。
HRESULT CMpcVideoRenderer::CheckMediaType(const CMediaType* pmt)
{
CheckPointer(pmt, E_POINTER);
CheckPointer(pmt->pbFormat, E_POINTER);
if (pmt->majortype == MEDIATYPE_Video && (pmt->formattype == FORMAT_VideoInfo2 || pmt->formattype == FORMAT_VideoInfo)) {
for (const auto& sudPinType : sudPinTypesIn) {
if (pmt->subtype == *sudPinType.clsMinorType) {
CAutoLock cRendererLock(&m_RendererLock);
if (!m_VideoProcessor->VerifyMediaType(pmt)) {
return VFW_E_UNSUPPORTED_VIDEO;
}
return S_OK;
}
}
}
return E_FAIL;
}
具体的实现就不讲解了,就是对媒体类型的验证。
总结:从输入PIN的媒体类型检查函数可知,虽然媒体类型的检查在输入PIN的ReceiveConnection中发起,但是最终的实现由输入PIN转给了对应包含它的Filter上。发现这种现象以后,我也看了输入PIN的其他几个实现函数(CompleteConnect、SetMediaType、Receive等),也是同样的实现方式。由此可知PIN只是负责暴露对外的连接接口,具体的业务逻辑都是转交给了Filter来实现。
本文深入探讨 DirectShow 的 CBasePin 类及其派生类中 CheckMediaType 函数的作用和实现。该函数用于验证输入的媒体类型是否符合PIN的要求,其具体实现通常在PIN的派生类中,如CSourceStream和CRendererInputPin。在输入PIN中,CheckMediaType函数的最终处理通常由包含它的Filter完成,如CMpcVideoRenderer。通过对源代码的分析,揭示了DirectShow中媒体类型协商和连接的内部机制。

被折叠的 条评论
为什么被折叠?



