locked
Stand-alone EVR Media Sink and memory leak RRS feed

  • Question

  • Hello,

    I am currently developping a media library (Linux/Windows) and I want to provide a video renderer for the Windows part.

    I read on the EVR Media Sink page (https://msdn.microsoft.com/en-us/library/windows/desktop/aa965265(v=vs.85).aspx) that "this component can be used as a standalone component", perfect ! So I have implemented it...and it works great, at least pictures are rendered on a window with the correct selected framerate.

    But I have a memory leak (reproductible on different computers) : 0.1 MB each 10~20 seconds.

    I have read, and read again the documentation but I am not able to find the culprit !!!

    Maybe someone has already encounter this ?

    I have a simple class which implements IMFAsyncCallback, here is main parts:

      HRESULT 
      Renderer::Invoke (IMFAsyncResult* result)
      {
        // See if the event indicates a failure.
        // If so, fail and return the MEError event.
        HRESULT hr = result->GetStatus ();
        if (FAILED (hr))
          return hr;
    
        // Get the event from the event queue.
        CComPtr<IMFMediaEvent> event;
        hr = streamsink_->EndGetEvent (result, &event);
        if (FAILED (hr))
          return hr;
    
        // Get the event type.
        MediaEventType type;
        hr = event->GetType (&type);
        if (FAILED (hr))
          return hr;
    
        // Request the next event.
        hr = streamsink_->BeginGetEvent (this, nullptr);
        if (FAILED (hr))
          return hr;
    
        switch (type)
        {
          case MEStreamSinkStopped: 
            SetEvent (stopsink_);
            break ;
    
          case MEStreamSinkRequestSample:
          {        
            InterlockedIncrement (&requests_);
            SetEvent (requestevent_);
            break ;
          }
    
          default:
          case MESinkUnknown:
            break ;
        }
    
        return hr;
      }

    Configuration:

        CComPtr<IMFMediaType> mt;
        MFCreateMediaType (&mt);
        if (nullptr == mt)
          return E_POINTER;
    
        UINT32 numerator = 0, denominator = 0;
        UINT64 duration = static_cast<__int64> (10000000 / media.fps);
        MFAverageTimePerFrameToFrameRate (duration, &numerator, &denominator);
    
        mt->SetGUID (MF_MT_SUBTYPE, media.fourcc);
        mt->SetGUID (MF_MT_MAJOR_TYPE, MFMediaType_Video);
        mt->SetUINT32 (MF_MT_DEFAULT_STRIDE, media.stride);
        mt->SetUINT32 (MF_MT_ALL_SAMPLES_INDEPENDENT, true);
        MFSetAttributeRatio (mt, MF_MT_FRAME_RATE, numerator, denominator);
        mt->SetUINT32 (MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
        MFSetAttributeSize (mt, MF_MT_FRAME_SIZE, media.width, media.height);
        MFSetAttributeRatio (mt, MF_MT_PIXEL_ASPECT_RATIO, media.ratioX, media.ratioY);
        
        CComPtr<IMFMediaTypeHandler> handler;
        HRESULT hr = streamsink_->GetMediaTypeHandler (&handler);
        if (FAILED (hr))
          return hr;
    
        // Try to set the media type on the
        // media type handler for the sink.
        hr = handler->SetCurrentMediaType (mt);
        if (FAILED (hr))
          return hr;
    
        CComPtr<IMFVideoSampleAllocator> allocator;
        hr = MFGetService (streamsink_, MR_VIDEO_ACCELERATION_SERVICE, IID_PPV_ARGS (&allocator));
        if (FAILED (hr))
          return hr;
    
        hr = allocator->InitializeSampleAllocator  (2, mt);
        if (FAILED (hr))
          return hr;
        
        //
        // ...then (re)start it.
        //
    
        // If media sink support preroll, use it.
        CComQIPtr<IMFMediaSinkPreroll> preroll = mediasink_;
        if (nullptr != preroll) 
        {
          hr = preroll->NotifyPreroll (0);
          if (FAILED (hr))
            return hr;
        }
    
        hr = streamsink_->BeginGetEvent (this, nullptr);
        if (FAILED (hr))
          return hr;
    Delivering thread:

        UINT64 delivered = 0;
        
        CComPtr<IMFVideoSampleAllocator> allocator;
        HRESULT hr = MFGetService (streamsink_, MR_VIDEO_ACCELERATION_SERVICE, IID_PPV_ARGS (&allocator));
        if (FAILED (hr))
          return ;
    
        HANDLE events[] = { stopthread_, requestevent_ };
        while (WAIT_OBJECT_0 != WaitForMultipleObjects (2, events, FALSE, INFINITE))
        {
          LONG requests = InterlockedExchange (&requests_, 0);
          while (requests > 0)
          {
            CComPtr<IMFSample> sample;
            hr = allocator->AllocateSample (&sample);
            if (FAILED (hr))
              break ;
            
            //
            // FIXME: Copy video data from input pool
            //
    
            sample->SetSampleTime (delivered*400000);
            hr = streamsink_->ProcessSample (sample);
     
            ++delivered;
            --requests;
          }
        }


    Windbg is showing the growing heap with a lot of this:

           ....

            0000021e1418cae0 000c 000c  [00]   0000021e1418cb10    00098 - (busy)
              MFPlat!CAsyncResult::`vftable'
            0000021e1418cba0 000c 000c  [00]   0000021e1418cbd0    00098 - (busy)
              MFPlat!CAsyncResult::`vftable'
            0000021e1418cc60 000c 000c  [00]   0000021e1418cc90    00098 - (busy)
              MFPlat!CMediaEventPooled::`vftable'
            0000021e1418cd20 000c 000c  [00]   0000021e1418cd50    00098 - (busy)
              MFPlat!CMediaEventPooled::`vftable'
            0000021e1418cde0 000c 000c  [00]   0000021e1418ce10    00098 - (busy)
              MFPlat!CAsyncResult::`vftable' 

            ....

    The resulting stack for one of those busy allocated heap is:

    0:015> !heap -p -a 0000021e1418d1d0
        address 0000021e1418d1d0 found in
        _HEAP @ 21e0e480000
                  HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state
            0000021e1418d1a0 000c 0000  [00]   0000021e1418d1d0    00098 - (busy)
              MFPlat!CAsyncResult::`vftable'
            7ffeaaed5b37 ntdll!RtlpCallInterceptRoutine+0x000000000000003f
            7ffeaaef7b93 ntdll!RtlpAllocateHeapInternal+0x000000000008cd13
            7ffea8779a00 msvcrt!malloc+0x0000000000000070
            7ffe92bc62b7 MFPlat!operator new+0x0000000000000023
            7ffe92bbee91 MFPlat!CEventResult::CreateInstance+0x0000000000000021
            7ffe92bbed8e MFPlat!CObjectPoolT<CEventResult,400>::CreateInstance<CEventResult,IMFMediaEvent * __ptr64>+0x00000000000000ce
    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SYSTEM32\MF.dll - 
            7ffe86903cd2 MF!MFGetLocalId+0x0000000000007a92
            7ffe86903c37 MF!MFGetLocalId+0x00000000000079f7
            7ffe868e9be4 MF!MFShutdownObject+0x0000000000004494
            7ffe868ea3f4 MF!MFShutdownObject+0x0000000000004ca4
            7ffe868e8e05 MF!MFShutdownObject+0x00000000000036b5
            7ffe985783d6 RTWorkQ!CSerialWorkQueue::QueueItem::ExecuteWorkItem+0x00000000000000b6
            7ffe98578d02 RTWorkQ!CSerialWorkQueue::OnReplyAsyncCallback::Release+0x0000000000000212
            7ffeaae83021 ntdll!TppWorkpExecuteCallback+0x0000000000000131
            7ffeaae81989 ntdll!TppWorkerThread+0x00000000000006c9
            7ffea90f2774 KERNEL32!BaseThreadInitThunk+0x0000000000000014
            7ffeaaeb0d51 ntdll!RtlUserThreadStart+0x0000000000000021

    Friday, August 11, 2017 9:04 PM