0
votes

I am experiencing a strange problem when trying to use the GMFBridge filter with the output of an Euresys UxH264 card.

I am trying to integrate this card into our solution, that relies on GMFBridge to handle the ability of continuous capture to multiple files, performing muxing and file-splitting without having to stop the capture graph.

This card captures video and audio from analog inputs. It provides a DirectShow filter exposing both a raw stream of the video input and a hardware-encoded H.264 stream. The audio stream is provided as an uncompressed stream only.

When I attempt to directly connect any of the output pins of the Euresys source filters to the input pins of the GMFBridge Sink, they get rejected, with the code VFW_E_NO_ALLOCATOR. (In the past I have successfully connected both H.264 and raw audio streams to the bridge).

Grasping at straws, I plugged in a pair of SampleGrabber filters between the Euresys card filters and the bridge sink filter, and -just like that- the connections between sample grabbers and sink were accepted.

However, I am not getting any packets on the other side of the bridge (the muxing graph). I inspected the running capture graph with GraphStudioNext and somehow the sample grabbers appear detached from my graph, even though I got successful confirmations when I connected them to the source filter!.

Here's the source code creating the graph.

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 is my HRESULT checking macro, it will raise a managed exception if the result is other than S_OK. So the connection between pins succeeded, but here is how the graph looks disjointed when it is running:

Disjoint Graph after running

So, I have three questions:

1) What could VFW_E_NO_ALLOCATOR mean in this instance? (the source filter can be successfully connected to other components such as LAV Video decoder or ffdshow video decoder).

2) Is there a known workaround to circumvent the VFW_E_NO_ALLOCATOR problem?

3) Is it possible that a filter gets disconnected at runtime as it seems to be happening in my case?

1

1 Answers

0
votes

I found a reference by Geraint Davies giving a reason as to why the GMFBridge sink filter may be rejecting the connection.

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.

With this information, I decided to create an ultra simple CTransformFilter filter that simply accepts the allocator offered by the bridge and copies to the output sample whatever comes in from the input sample. I am not 100% sure that what I did was right, but it is working now. I could successfully plug-in the Euresys card as part of my capture infrastructure.

For reference, if anyone experiences something similar, here is the code of the filter I created:

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;
}

Here is how I inserted the filter in the capture graph:

   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)
   );

Now, I still have no idea why inserting the sample grabber instead did not work and resulted in detached graphs. I corroborated this quirk by examining the graphs with Graphedit Plus too. If anyone can offer me an explanation, I would be very grateful indeed.