Ask a questionAsk a question
 

AnswerIMediaSeeking SetPositions hangs app

  • Tuesday, November 03, 2009 11:51 AMvvkmlhtr Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code

    I am using a CWnd derived window CMyWnd and using VMR9 in renderless mode using custom allocator-presenter to display a video file inside CMyWnd. 

    My DShow graph is created in CMyWnd. Everything works ok when the graph is playing. Any seeking performed is done without any problem. But when the graph is in paused state and if I call IMediaSeeking::SetPositions, the application hangs. There seems to be some kind of deadlock. I am not creating any thread but directshow internally uses threads to stream data and somewhere it is stopping the main thread.

     CComQIPtr<IMediaSeeking> pMS(m_pGB);
     if( pMS )
     {
       LONGLONG llCurTime;
       HRESULT hr;
       pMS->GetCurrentPosition(&llCurTime);
       hr = pMS->SetPositions (&llCurTime, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
     }
    

    The video file I am playing is an AVI file. I am using SetPositions only to flush the renderer because sometimes the source / destination rect change and renderer does not update the frame. By flushing the frames cued inside renderer, it works correctly. I know it's not the right way to flush but more disturbing is the problem of application hang.

     

    ----------------------------- My Call stack ------------------ 

    ntdll.dll!_KiFastSystemCallRet@0()  
    ntdll.dll!_NtWaitForMultipleObjects@20() + 0xc bytes
    KernelBase.dll!_WaitForMultipleObjectsEx@20() - 0x54 bytes
    kernel32.dll!_WaitForMultipleObjectsExImplementation@20() + 0x8e bytes
    user32.dll!_RealMsgWaitForMultipleObjectsEx@20() + 0xd7 bytes
    user32.dll!_MsgWaitForMultipleObjects@20() + 0x1f bytes
    GdiPlus.dll!BackgroundThreadProc() + 0x59 bytes
    GdiPlus.dll!DllRefCountSafeThreadThunk() + 0x10 bytes
    kernel32.dll!@BaseThreadInitThunk@12() + 0x12 bytes
    ntdll.dll!___RtlUserThreadStart@8() + 0x27 bytes
    ntdll.dll!__RtlUserThreadStart@8() + 0x1b bytes

     

     ------------ Threads -------------------

    0 1920 Main Thread Main Thread MyVideoRenderWnd::FlushData() Normal 0
    0 > 860 Worker Thread DllRefCountSafeThreadThunk _KiFastSystemCallRet@0 Normal 0 <------------------------- Stuck here.
    0 5444 Worker Thread _TppWaiterpThread@4 _KiFastSystemCallRet@0 Normal 0
    0 3216 Worker Thread _beginthreadex AfxInternalPumpMessage Normal 0
    0 3780 Worker Thread _TppWorkerThread@4 _KiFastSystemCallRet@0 Normal 0
    0 2688 RPC Thread RPC Callback Thread _KiFastSystemCallRet@0 Normal 0
    0 2664 Worker Thread _TppWorkerThread@4 _KiFastSystemCallRet@0 Normal 0


    Please suggest.

Answers

  • Thursday, November 05, 2009 3:46 PMChris P_MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Sounds like a synchronization problem in the VMR between the streaming thread and the seeking thread (your thread).  Synchronization of threads is probably the biggest pitfall in DirectShow.  

    You should be careful of your tight while loop however as it will consume 100% CPU on the thread that it is executing on, and on a single core CPU that wont play nice with the other threads.  You can fix that by making the thread yield with Sleep(0) inside the while loop.

    www.chrisnet.net
    • Marked As Answer byvvkmlhtr Monday, November 09, 2009 10:18 AM
    •  
  • Thursday, November 05, 2009 10:15 AMvvkmlhtr Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     AnswerHas Code

    I don't know what your custom allocator/presenter is doing but it is apparently doing something wrong.  I'm under the assumption that the rest of the filters are stock filters with the exception of possibly a video decoder?  You can prove that it is your allocator/presenter that is the problem by using the VMR in Windowed/Windowless mode and see if the problem still presents itself.

    Yes, the problem is with allocator/presenter and it works otherwise.
    I am only using the vmr9allocator sample implementation.

    I did some more debugging and found that
     in PresentImage function
    is the probable culprit as app hangs inside this function.

    m_lpIVMRSurfAllocNotify->
    ChangeD3DDevice( m_D3DDev, hMonitor )
    

    This function is used to tell VMR that d3d device has changed. Now the scenario is that whenever window size changes (main window into which VMR renders), I recreate the device and all resources (a necessity). For that I use a variable to notify VMR to recreate surfaces. If that variable is true, then recreate surfaces.
    Also, whenever window size changes I call my SetPositions code to flush frames inside VMR.

    I have observed --
    If I do not set the variable to recreate surfaces, then no hang.
    If i do not call SetPositions, then no hang.
    But if I set the variable either before SetPositions or after SetPositions, app hangs waiting somewhere for some critical section.

    I am calling SetPositions from a different thread (main thread). I also understand that streaming thread is blocked by VMR in paused state because all its buffers are filled. Now SetPositions flushes those buffers

    Things are looking less chaotic now but still no clue to the real problem.


    ***Update***
    I think I have found a fix.
    There is one thread on which SetPositions is flushing the VMR.
    And there is another thread (streaming thread?) on which the device and surfaces are recreated inside PresentImage function.
    I observed that as size is changed, SetPositions is repeatedly called on one thread and in another thread, direct 3d device is being destroyed and recreated. These two tasks are going on simultaneously and somewhere app hangs.

    I put a variable (m_bFix = TRUE) before I recreated device and surfaces in present image and (m_bFix = FALSE) when I am finished doing it.
    And in other thread, before I call SetPositions, I put this:
    while( m_pAllocator->m_bFix);
    
    and I have not yet seen my app hang since then.
    • Marked As Answer byvvkmlhtr Monday, November 09, 2009 10:18 AM
    •  

All Replies

  • Tuesday, November 03, 2009 3:01 PMChris P_MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    When the graph is paused the renderer is blocking the streaming thread by holding references to the samples.  When a seek occurs the input pin of the renderer receives a BeginFlush (on a separate thread) and at that time the renderer should discard all held samples by releasing them.  You should not accept any new samples to queue until EndFlush has been called.
    www.chrisnet.net
  • Wednesday, November 04, 2009 2:25 AMvvkmlhtr Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    Thanks for the answer, Chris.

    > You should not accept any new samples to queue until EndFlush has been called.

    I am not sure I understand that. Isn't VMR9 suposed to automatically reject new samples until EndFlush is called? I have not written my own custom filter where I can control this kind of behavior? Also, before calling SetPositions, the graph is in paused state and I make sure that pause is completed before SetPositions is called. I am not pushing or accepting any new samples. I was going through this sample http://msdn.microsoft.com/en-us/library/ms867199.aspx and I found this code -


    // SEEKING
    void CBallStream::UpdateFromSeek()
    {
    
        if (ThreadExists()) 
        {
            // next time around the loop, the worker thread will
            // pick up the position change.
            // We need to flush all the existing data - we must do that here
            // as our thread will probably be blocked in GetBuffer otherwise
            
            DeliverBeginFlush();
            // make sure we have stopped pushing
            Stop();
            // complete the flush
            DeliverEndFlush();
            // restart
            Run();
        }
    
    }


    They are adding seeking capabilties to the filter and although this is not related to my problem, but they have written something about "thread blocked in GetBuffer".

    I still have not understood the reason for main app thread blocking. I have few doubts -

    1. Is there any minimum time-gap between two consecutive calls to SetPositions seeking at same position. i.e. If the current stream position is 0, and I make a call to SetPositions to reset the stream to position 0, will there be any problem?
    I have seen that my first call to SetPosition normally succeds in pause state but the second call usually blocks.

    2. Does it matter which frame I am seeking to? I have seen that if I pause at some stream positions and then repeatedly call SetPositions to reset at that position, then no blocking occurs. But at some other positions, app blocks.

    3. What other method I can use to make renderer let go of cued frames in paused state. I tried calling BeginFlush() and EndFlush() on source filter output pin but it does not update the renderer with new display settings like SetPositions do.


    CComPtr<IPin> pOPPin;
    FindPin(pSrcFilter, PINDIR_OUTPUT, &pOPPin);
    if(pOPPin)
    {
      hr = pOPPin->BeginFlush(); //S_OK returned
      hr = pOPPin->EndFlush();   //S_OK.
    }
    

  • Wednesday, November 04, 2009 7:00 PMChris P_MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    1.  There is no minimum.

    2.  No.

    3.  There is no other method other than stop, which obviously means your not paused anymore.


    When you are paused the streaming thread (owned by the source/parser) is blocked, typically in the call to GetBuffer() once all the available samples are queued up in the renderer.  On seek the renderer should release these samples in the BeginFlush() so that the streaming thread becomes unblocked.  You never call BeginFlush() directly, this is done by the filter implementing IMediaSeeking.

    I don't know what your custom allocator/presenter is doing but it is apparently doing something wrong.  I'm under the assumption that the rest of the filters are stock filters with the exception of possibly a video decoder?  You can prove that it is your allocator/presenter that is the problem by using the VMR in Windowed/Windowless mode and see if the problem still presents itself.

    www.chrisnet.net
  • Thursday, November 05, 2009 10:15 AMvvkmlhtr Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     AnswerHas Code

    I don't know what your custom allocator/presenter is doing but it is apparently doing something wrong.  I'm under the assumption that the rest of the filters are stock filters with the exception of possibly a video decoder?  You can prove that it is your allocator/presenter that is the problem by using the VMR in Windowed/Windowless mode and see if the problem still presents itself.

    Yes, the problem is with allocator/presenter and it works otherwise.
    I am only using the vmr9allocator sample implementation.

    I did some more debugging and found that
     in PresentImage function
    is the probable culprit as app hangs inside this function.

    m_lpIVMRSurfAllocNotify->
    ChangeD3DDevice( m_D3DDev, hMonitor )
    

    This function is used to tell VMR that d3d device has changed. Now the scenario is that whenever window size changes (main window into which VMR renders), I recreate the device and all resources (a necessity). For that I use a variable to notify VMR to recreate surfaces. If that variable is true, then recreate surfaces.
    Also, whenever window size changes I call my SetPositions code to flush frames inside VMR.

    I have observed --
    If I do not set the variable to recreate surfaces, then no hang.
    If i do not call SetPositions, then no hang.
    But if I set the variable either before SetPositions or after SetPositions, app hangs waiting somewhere for some critical section.

    I am calling SetPositions from a different thread (main thread). I also understand that streaming thread is blocked by VMR in paused state because all its buffers are filled. Now SetPositions flushes those buffers

    Things are looking less chaotic now but still no clue to the real problem.


    ***Update***
    I think I have found a fix.
    There is one thread on which SetPositions is flushing the VMR.
    And there is another thread (streaming thread?) on which the device and surfaces are recreated inside PresentImage function.
    I observed that as size is changed, SetPositions is repeatedly called on one thread and in another thread, direct 3d device is being destroyed and recreated. These two tasks are going on simultaneously and somewhere app hangs.

    I put a variable (m_bFix = TRUE) before I recreated device and surfaces in present image and (m_bFix = FALSE) when I am finished doing it.
    And in other thread, before I call SetPositions, I put this:
    while( m_pAllocator->m_bFix);
    
    and I have not yet seen my app hang since then.
    • Marked As Answer byvvkmlhtr Monday, November 09, 2009 10:18 AM
    •  
  • Thursday, November 05, 2009 3:46 PMChris P_MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Sounds like a synchronization problem in the VMR between the streaming thread and the seeking thread (your thread).  Synchronization of threads is probably the biggest pitfall in DirectShow.  

    You should be careful of your tight while loop however as it will consume 100% CPU on the thread that it is executing on, and on a single core CPU that wont play nice with the other threads.  You can fix that by making the thread yield with Sleep(0) inside the while loop.

    www.chrisnet.net
    • Marked As Answer byvvkmlhtr Monday, November 09, 2009 10:18 AM
    •  
  • Monday, November 09, 2009 10:18 AMvvkmlhtr Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Thanks for the answer, Chris. I have changed my while loop as told by you although I am afraid to admit that I do not understand much why executing a separate function call - Sleep - with 0 wait is better or faster. To me, it appears useless ( :embarassed: )  as OS will anyway switch threads after some time slice interval. 

    I appreciate your efforts in answering the questions. I remember asking a dshow related question more than 2 years back and it was solved by you and after two years, things have not changed much :-) Some day you should get a lifetime achievement award.

    Thanks again and keep answering.
  • Wednesday, November 11, 2009 2:23 PMChris P_MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Thanks for the kind words.

    You are correct that the OS will eventually switch the CPU thread context, however any thread that is executing rapidly without a yield of any sort (Sleep(), WaitForxxx(), blocking read etc) will end up receiving more time slices than the other threads executing on the system because it is always in a ready to execute state.  So for a short time your thread can receive 99% of the CPU's attention which will slow down the execution of other threads which might have something meaningful to do.  So for that reason if your thread has nothing important to do for a while it should yield the remainder of it's time slice to let other threads execute.  If all the other threads are idle as well then the CPU doesn't have to work as hard and will consume less power, it's like being green at the micro level. ;-)

    www.chrisnet.net