Search code examples
c++comdirectshow

Directshow Application crash after exiting of main()


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.


Solution

  • 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.