-1

I am writing directshow application. Below is the code that works fine but crashes with the error message "App.exe has stopped working". The entire code is written below. Please note that I am using Windows SDK 7.0 which does not have atlbase.h and hence I cannot use CComPtr<IBaseFilter> myFilter; type of pointer declaration which is supposed to clear memory at exit.

EDIT: The application crashes only if I connect all filters explicitly. In this case, the destructor of my filter is not called. If I just connect source filter to renderer (which will internally connect my filter and a demux filter), the destructor of my filter is called and there is no crash. I have put the macro MANUAL_CONNECT over the code which causes the crash. I have removed RemoveFilter call and replaced it with Release call.

I am writing the application code here:

void main(WORD32 argc, CHAR *argv[])
{
        // Initialize the COM library.
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        printf("ERROR - Could not initialize COM library");
        return;
    }
    {
        IGraphBuilder           *pGraph = NULL;
        IFileSourceFilter       *pFileSourceFilter = NULL;
        IMediaControl           *pControl = NULL;
        IMediaEvent             *pEvent = NULL;
        IBaseFilter             *pSource = NULL;
        IBaseFilter             *pVideoDecode = NULL;
        IBaseFilter             *pVideoRenderer = NULL;
        IEnumPins               *pEnumPins = NULL;
        IPin                    *pPinIn = NULL;
        IPin                    *pPinOut = NULL;
        ULONG                   fetched;
        PIN_INFO                PinInfo;    
        IEnumFilters            *pEnum = NULL;
        BOOL                    stop = FALSE;
        int i;

        // Create the filter graph manager and query for interfaces.
        hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);

        // Create the filter graph manager and query for interfaces.
        hr = CoCreateInstance(CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pSource);

        hr = pGraph->AddFilter(pSource, NULL);

        hr = pSource->QueryInterface(IID_IFileSourceFilter, (void**)&pFileSourceFilter);

        hr = pFileSourceFilter->Load(L"input.mp4", NULL);

        // Create Ittiam HEVC Decoder instance
        hr = CoCreateInstance(CLSID_ivdec, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pVideoDecode);

        // Create Video Renderer instance. We have used default video renderer
        hr = CoCreateInstance(CLSID_VideoRendererDefault, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pVideoRenderer);

        // Add decoder filter to the filter graph
        hr = pGraph->AddFilter(pVideoDecode, NULL);

        // Add renderer filter to the filter graph
        hr = pGraph->AddFilter(pVideoRenderer, NULL);

        /**************************************************************/
        /* -- Connecting source filter to demux filter starts here -- */
        /**************************************************************/
        // Enumerate pins of the source filter
        hr = pSource->EnumPins(&pEnumPins);
        hr = pEnumPins->Reset();
        // Get pin of source filter. Source filter has only output pin, so no check required
        hr = pEnumPins->Next(1, &pPinOut, &fetched);
        hr = pEnumPins->Release();
#if MANUAL_CONNECT
        // Enumerate pins of the decoder filter
        hr = pVideoDecode->EnumPins(&pEnumPins);
        hr = pEnumPins->Reset();
        // Get pin of decoder filter. Decoder filter has 2 pins, so ensure the selected pin is input pin.
        // If not, get another pin
        hr = pEnumPins->Next(1, &pPinIn, &fetched);
        hr = pPinIn->QueryPinInfo(&PinInfo);
        if(PINDIR_OUTPUT == PinInfo.dir)
        {
            hr = pPinIn->Release();
            hr = pEnumPins->Next(1, &pPinIn, &fetched);
        }

        // Connect output pin of demux filter to input pin of decoder filter
        hr = pGraph->Connect(pPinOut, pPinIn);

        /*************************************************************/
        /* -- Connecting demux filter to decoder filter ends here -- */
        /*************************************************************/

        /******************************************************************/
        /* -- Connecting decoder filter to renderer filter starts here -- */
        /******************************************************************/
        // Enumerate pins of the decoder filter
        hr = pVideoDecode->EnumPins(&pEnumPins);
        hr = pEnumPins->Reset();
        // Get pin of decoder filter. Decoder filter has 2 pins, so ensure the selected pin is output pin.
        // If not, get another pin
        hr = pEnumPins->Next(1, &pPinOut, &fetched);
        hr = pPinOut->QueryPinInfo(&PinInfo);
        if(PINDIR_INPUT == PinInfo.dir)
        {
            hr = pPinOut->Release();
            hr = pEnumPins->Next(1, &pPinOut, &fetched);
        }
        hr = pEnumPins->Release();
#endif

        // Enumerate pins of the renderer filter
        hr = pVideoRenderer->EnumPins(&pEnumPins);
        hr = pEnumPins->Reset();
        // Get pin of renderer filter. Renderer filter has only input pin, so no check required
        hr = pEnumPins->Next(1, &pPinIn, &fetched);
        hr = pPinIn->QueryPinInfo(&PinInfo);

        // Connect output pin of decoder filter to input pin of renderer filter
        hr = pGraph->Connect(pPinOut, pPinIn);

        /****************************************************************/
        /* -- Connecting decoder filter to renderer filter ends here -- */
        /****************************************************************/

        hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
        hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);

        // Run the graph.
        hr = pControl->Run();

        if (SUCCEEDED(hr))
        {
            // Wait for completion.
            long evCode;
            pEvent->WaitForCompletion(INFINITE, &evCode);

            // Note: Do not use INFINITE in a real application, because it
            // can block indefinitely.
        }

        hr = pControl->Stop();

        hr = pSource->Release();
        hr = pVideoDecode->Release();
        hr = pControl->Release();
        hr = pEvent->Release();
        hr = pGraph->Release();
    }

    CoUninitialize();
    printf("Exiting main!!\n");
}

I have removed error checks from the post but I have all error checks in my code. I can see the Exiting main!! print but than the application crashes. Any suggestions on how to debug this? Please let me know if any information is missing. I am using Microsoft Visual C++ 2010 Express for my development.

Saurabh
  • 85
  • 1
  • 9
  • You are only checking for errors in like *two* places. It does no good to save the HRESULT value returned from the function if you aren't going to bother to see if it indicates success or failure. – Cody Gray - on strike Mar 02 '16 at 08:24

1 Answers1

2

You must terminate all COM activity (specifically: release all COM interface pointers) on the thread before calling CoUninitialize, you don't do it.

See, for example, this code and its Release calls at the _tmain bottom.

It makes sense to use more recent version of Visual Studio (2013, 2015), where free community edition already includes ATL and you can enjoy automatic COM interface reference management using CComPtr and friends. This is the first advise to those who uses raw COM interface pointers and experience issues managing them incorrectly.

See also:

UPDATE: Using raw pointers inaccurately, you keep having leaks:

    hr = pGraph->Connect(pPinOut, pPinIn);
    // ...
    hr = pEnumPins->Next(1, &pPinOut, &fetched);

IEnumPin::Next call overwrites pPinOut pointer and leaks a reference.

Community
  • 1
  • 1
Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • When I simply call `hr = pGraph->RenderFile(L"input.mp4", NULL);`, there is no crash. I noticed that my filter's destructor is not being called when I add the filters manually. Any suggestion what might be going wrong? My filter's destructor is being called when directly calling RenderFile. – Saurabh Mar 01 '16 at 12:44
  • Problem found but not sure of the solution. When I manually connect input pin of my filter to file source and output pin to renderer, there is a crash when releasing my filter. If I connect file source to renderer directly (which will internally connect my filter), there is no crash and there is clean exit. How can I manually connect the pins and still ensure clean exit? – Saurabh Mar 01 '16 at 14:16
  • It would be good idea to update source code in the question to reflect your progress in COM interface management. You have not yet fixed the original problem. – Roman R. Mar 01 '16 at 14:20
  • I have edited the original question with current progress. – Saurabh Mar 02 '16 at 07:09
  • 1
    @Saurabh there is also a leaked `IBaseFilter` after `pPinIn->QueryPinInfo(&PinInfo)`. And you don't release `pVideoRenderer`, `pFileSourceFilter`, ... – wimh Mar 02 '16 at 22:34
  • Thanks [Wimmel](http://stackoverflow.com/users/33499/wimmel), that completes the answer. – Saurabh Mar 07 '16 at 14:40