连接到 GMFBridge 水槽过滤器输入引脚的问题

Problems connecting to the input pins of GMFBridge Sink Filter

我在尝试将 GMFBridge 过滤器与 Euresys UxH264 卡的输出一起使用时遇到了一个奇怪的问题。

我正在尝试将此卡集成到我们的解决方案中,该解决方案依赖于 GMFBridge 处理连续捕获多个文件的能力,执行多路复用和文件拆分而无需停止捕获图。

此卡从模拟输入捕获视频和音频。它提供了一个 DirectShow 过滤器,用于公开视频输入的原始流和硬件编码的 H.264 流。音频流仅作为未压缩流提供。

当我尝试将 Euresys 源滤波器的任何输出引脚直接连接到 GMFBridge 接收器的输入引脚时,它们被拒绝,代码为 VFW_E_NO_ALLOCATOR。 (过去我已经成功地将 H.264 和原始音频流连接到桥)。

抓住吸管,我在 Euresys 卡过滤器和桥接水槽过滤器之间插入了一对 SampleGrabber 过滤器,并且 - 就这样 - 接受了样本采集器和水槽之间的连接。

但是,我在网桥的另一端(复用图)没有收到任何数据包。我用 GraphStudioNext 检查了 运行 捕获图,不知何故 样本采集器似乎与我的图分离,即使我在将它们连接到源过滤器时得到了成功的确认!.

这是创建图形的源代码。

void EuresysSourceBox::BuildGraph() {
   HRESULT hRes;

   CComPtr<IGraphBuilder> pGraph;
   COM_CALL(pGraph.CoCreateInstance(CLSID_FilterGraph));
   #ifdef REGISTER_IN_ROT
      _rotEntry1 = FilterTools::RegisterGraphInROT(IntPtr(pGraph), "euresys graph");
   #endif

   // [*Video Source*]

   String^ filterName = "Ux H.264 Visual Source";
   Guid category = _GUIDToGuid((GUID)AM_KSCATEGORY_CAPTURE);
   FilterMonikerList^ videoSourceList = FilterTools::GetFilterMonikersByName(category, filterName);
   CComPtr<IBaseFilter> pVideoSource;
   int monikerIndex = config->BoardIndex;  // a filter instance will be retrieved for every installed board
   clr_scoped_ptr<CComPtr<IMoniker>>^ ppVideoSourceMoniker = videoSourceList[monikerIndex];

   COM_CALL((*ppVideoSourceMoniker->get())->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pVideoSource));
   COM_CALL(pGraph->AddFilter(pVideoSource, L"VideoSource"));

   // [Video Source]
   //
   // [*Audio Source*]

   filterName = "Ux H.264 Audio Encoder";
   FilterMonikerList^ audioSourceList = FilterTools::GetFilterMonikersByName(category, filterName);
   CComPtr<IBaseFilter> pAudioSource;
   clr_scoped_ptr<CComPtr<IMoniker>>^ ppAudioSourceMoniker = audioSourceList[monikerIndex];
   COM_CALL((*ppAudioSourceMoniker->get())->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pAudioSource));
   COM_CALL(pGraph->AddFilter(pAudioSource, L"AudioSource"));

   CComPtr<IPin> pVideoCompressedOutPin(FilterTools::GetPin(pVideoSource, "Encoded"));
   CComPtr<IPin> pAudioOutPin(FilterTools::GetPin(pAudioSource, "Audio"));

   CComPtr<IBaseFilter> pSampleGrabber;
   COM_CALL(pSampleGrabber.CoCreateInstance(CLSID_SampleGrabber));
   COM_CALL(pGraph->AddFilter(pSampleGrabber, L"SampleGrabber"));
   CComPtr<IPin> pSampleGrabberInPin(FilterTools::GetPin(pSampleGrabber, "Input"));
   COM_CALL(pGraph->ConnectDirect(pVideoCompressedOutPin, pSampleGrabberInPin, NULL));    // DOES NOT FAIL!!


   CComPtr<IBaseFilter> pSampleGrabber2;
   COM_CALL(pSampleGrabber2.CoCreateInstance(CLSID_SampleGrabber));
   COM_CALL(pGraph->AddFilter(pSampleGrabber2, L"SampleGrabber2"));
   CComPtr<IPin> pSampleGrabber2InPin(FilterTools::GetPin(pSampleGrabber2, "Input"));
   COM_CALL(pGraph->ConnectDirect(pAudioOutPin, pSampleGrabber2InPin, NULL));             // DOES NOT FAIL!!

   // [Video Source]---
   //                  |-->[*Bridge Sink*]
   // [Audio Source]---


   CComPtr<IPin> pSampleGrabberOutPin(FilterTools::GetPin(pSampleGrabber, "Output"));
   CComPtr<IPin> pSampleGrabber2OutPin(FilterTools::GetPin(pSampleGrabber2, "Output"));

   CreateGraphBridge(
      IntPtr(pGraph),
      IntPtr(pSampleGrabberOutPin),
      IntPtr(pSampleGrabber2OutPin)
   );

   // Root graph to parent object
   _ppCaptureGraph.reset(new CComPtr<IGraphBuilder>(pGraph));
}

COM_CALL 是我的 HRESULT 检查宏,如果结果不是 S_OK,它将引发托管异常。所以管脚之间的连接成功了,但这里是图表在 运行:

时看起来脱节的样子

所以,我有三个问题:

1) 在这种情况下,VFW_E_NO_ALLOCATOR 意味着什么?(源过滤器可以成功连接到其他组件,如 LAV 视频解码器或 ffdshow 视频解码器) .

2) 是否有已知的解决方法来规避 VFW_E_NO_ALLOCATOR 问题?

3) 过滤器是否可能在运行时断开连接,就像我的情况一样?

found a reference by Geraint Davies 给出 GMFBridge 接收器过滤器拒绝连接的原因。

It looks as though the parser is insisting on using its own allocator -- this is common with parsers where the output samples are merely pointers into the input samples. However, the bridge cannot implement suspend mode without using its own allocator, so a copy is required.

有了这些信息,我决定创建一个超简单的 CTransformFilter 过滤器,它只接受桥接器提供的分配器,并将来自输入样本的任何内容复制到输出样本。我不是 100% 确定我所做的是正确的,但它现在正在工作。我可以成功地将 Euresys 卡插入我的采集基础设施中。

作为参考,如果有人遇到类似的事情,这里是我创建的过滤器的代码:

class SampleCopyGeneratorFilter : public CTransformFilter {
protected:
   HRESULT CheckInputType(const CMediaType* mtIn) override { return S_OK; }
   HRESULT GetMediaType(int iPosition, CMediaType* pMediaType) override;
   HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut) override { return S_OK; }
   HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp) override;
   HRESULT Transform(IMediaSample *pSource, IMediaSample *pDest) override;
public:
   SampleCopyGeneratorFilter();
};

//--------------------------------------------------------------------------------------------------------------------

SampleCopyGeneratorFilter::SampleCopyGeneratorFilter() 
   : CTransformFilter(NAME("SampleCopyGeneratorFilter"), NULL, GUID_NULL)       
{

}


HRESULT SampleCopyGeneratorFilter::GetMediaType(int iPosition, CMediaType* pMediaType) {
   HRESULT hRes;
   ASSERT(m_pInput->IsConnected());
   if( iPosition < 0 )
      return E_INVALIDARG;

   CComPtr<IPin> connectedTo;
   COM_CALL(m_pInput->ConnectedTo(&connectedTo));

   CComPtr<IEnumMediaTypes> pMTEnumerator;
   COM_CALL(connectedTo->EnumMediaTypes(&pMTEnumerator));
   AM_MEDIA_TYPE* pIteratedMediaType;
   for( int i = 0; i <= iPosition; i++ ) {
      if( pMTEnumerator->Next(1, &pIteratedMediaType, NULL) != S_OK )
         return VFW_S_NO_MORE_ITEMS;
      if( i == iPosition )
         *pMediaType = *pIteratedMediaType;
      DeleteMediaType(pIteratedMediaType);
   }
   return S_OK;
}


HRESULT SampleCopyGeneratorFilter::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp) {
   HRESULT hRes;
   AM_MEDIA_TYPE mt;
   COM_CALL(m_pInput->ConnectionMediaType(&mt));
   try {
      BITMAPINFOHEADER* pBMI = HEADER(mt.pbFormat);
      pProp->cbBuffer = DIBSIZE(*pBMI);   // format is compressed, uncompressed size should be enough
      if( !pProp->cbAlign )
         pProp->cbAlign = 1;
      pProp->cbPrefix = 0;
      pProp->cBuffers = 4;

      ALLOCATOR_PROPERTIES actualProperties;
      COM_CALL(pAlloc->SetProperties(pProp, &actualProperties));
      if( pProp->cbBuffer > actualProperties.cbBuffer )
         return E_FAIL;
      return S_OK;
   } finally{
      FreeMediaType(mt);
   }
}


HRESULT SampleCopyGeneratorFilter::Transform(IMediaSample *pSource, IMediaSample *pDest) {
   HRESULT hRes;
   BYTE* pBufferIn;
   BYTE* pBufferOut;
   long destSize = pDest->GetSize();
   long dataLen = pSource->GetActualDataLength();
   if( dataLen > destSize )
      return VFW_E_BUFFER_OVERFLOW;
   COM_CALL(pSource->GetPointer(&pBufferIn));
   COM_CALL(pDest->GetPointer(&pBufferOut));
   memcpy(pBufferOut, pBufferIn, dataLen);
   pDest->SetActualDataLength(dataLen);
   pDest->SetSyncPoint(pSource->IsSyncPoint() == S_OK);
   return S_OK;
}

以下是我在捕获图中插入过滤器的方式:

   CComPtr<IPin> pAACEncoderOutPin(FilterTools::GetPin(pAACEncoder, "XForm Out"));
   CComPtr<IPin> pVideoSourceCompressedOutPin(FilterTools::GetPin(pVideoSource, "Encoded"));

   CComPtr<IBaseFilter> pSampleCopier;
   pSampleCopier = new SampleCopyGeneratorFilter();
   COM_CALL(pGraph->AddFilter(pSampleCopier, L"SampleCopier"));
   CComPtr<IPin> pSampleCopierInPin(FilterTools::GetPin(pSampleCopier, "XForm In"));
   COM_CALL(pGraph->ConnectDirect(pVideoSourceCompressedOutPin, pSampleCopierInPin, NULL));
   CComPtr<IPin> pSampleCopierOutPin(FilterTools::GetPin(pSampleCopier, "XForm Out"));


   CreateGraphBridge(
      IntPtr(pGraph),
      IntPtr(pSampleCopierOutPin),
      IntPtr(pAACEncoderOutPin)
   );

现在,我仍然不知道为什么插入样本采集器不起作用并导致分离图。我也通过使用 Graphedit Plus 检查图表证实了这个怪癖。如果有人能给我一个解释,我将不胜感激