Archive for July, 2009

Published by Roman on 31 Jul 2009

XviD video encoder and window messages from a worker thread

While encoding video, Xvid Video Encoder provides an optional status window displaying information on encoding progress.

Xvid Encoder Status Window

Since streaming typically takes place in a worker thread, with possibly no windows at all, and no message pump, the window is to be created on GUI thread and the encoder needs to synchronize progress updates with the window.

Such synchronization is a typical point where a deadlock can occur. Carelessly a window message may be sent in a blocking way so that worker thread is waiting while the message is processed on the window thread. If this sending happens at the moment of GUI thread trying to stop worker thread, a deadlock takes place. This problem often comes up when message sending takes place indirectly, e.g. being wrapped by an API call, such as SetWindowText, or as it can be seen below IsDlgButtonChecked.

In order to avoid deadlocks one should never SendMessage from a worker thread. Instead there should be a PostMessage with possibly a wait function which waits for either synchronization event (set by window) or worker thread termination request:

{
  CLock Lock(m_CriticalSection);
  m_sText = sText;
  m_SynchronizationEvent.Reset();
}
PostMessage(...);
HANDLE phObjects[] = { ThreadTerminationEvent, m_SynchronizationEvent };
const DWORD nWaitResult =  WaitForMultipleObjects(..., phObjects, ...);
if(nWaitResult == WAIT_OBJECT_0 + 1 ) // m_SynchronizationEvent
{
  CLock Lock(m_CriticalSection);
  // ...
}

Back to Xvid encoder: trying to abort encoding process, the application deadlocks. Thread checking shows worker thread state:

Xvid Video Encoder Deadlocked Thread Call Stack

That is, a described deadlock in action.

While it is already made this way letting deadlock occur, is there any workaround to avoid locking? Yes, there is. Stopping encoding, GUI thread should keep pumping messages while waiting for worker thread termination. Before closing worker thread handle, one needs to signal the thread to terminate and wait using thread handle with MsgWaitForMultipleObjects, so that messages possibly sent while terminating the thread are dispatched to target windows.

Published by Roman on 30 Jul 2009

Combo Box selection, WM_SETREDRAW and CB_SETCURSEL

Given the combo box initialization code:

m_ComboBox.SetRedraw(FALSE);
m_ComboBox.ResetContent();
for(INT nIndex = 0; nIndex < 3; nIndex++)
    m_ComboBox.AddString(AtlFormatString(_T("Item %d"), nIndex + 1));
m_ComboBox.SetCurSel(1);
m_ComboBox.SetRedraw(TRUE)

How the combo box is going to look like?

Continue Reading »

Published by Roman on 23 Jul 2009

IAMGraphBuilderCallback and Intelligent Connect tuning

There was a question asked about how IAMGraphBuilderCallback interface is used to prevent from particular filter insertion during Intelligent Connect.

First of all, there is sample code at The March Hare’s website:

The IAMGraphBuilderCallback class can be used to remove problematic dshow filters when building a graph with intelligent connect.  This sample does not provide the UI to blacklist filters but will give you a better head start than the information in the documentation.  See the header file for an example of how to use it in your code.  Add your own code to the SelectedFilter or CreatedFilter methods to remove any unwanted filters.

The idea behind the method can be easily illustrated with a code snippet. Each time Filter Graph Manager considers trying a filter, before its instantiation it calls (if provided) IAMGraphBuilderCallback::SelectedFilter method, where there is an option to reject it (before it even was instantiated using CoCreateInstance).

The code snippet below checks selected filter and instructs to reject it if it is an FFDshow Audio Decoder filter.

// IAMGraphBuilderCallback
STDMETHOD(SelectedFilter)(IMoniker* pMoniker) throw()
{
    ATLTRACE(atlTraceCOM, 4, _T("...\n"));
    _ATLTRY
    {
        ATLASSERT(pMoniker);
        const CStringW sMonikerDisplayName = _FilterGraphHelper::GetMonikerDisplayName(pMoniker, NULL);
        ATLTRACE(atlTraceGeneral, 4, _T("sMonikerDisplayName %ls\n"), sMonikerDisplayName);
        static const LPCTSTR g_pszFfdshowAudioDecoderClassIdentifier = _T("{0F40E1E5-4F79-4988-B1A9-CC98794E6B55}");
        if(sMonikerDisplayName.Find(CStringW(g_pszFfdshowAudioDecoderClassIdentifier)) >= 0)
            return E_FAIL; // Die!
    }
    _ATLCATCH(Exception)
    {
        return Exception;
    }
    return S_OK;
}

Published by Roman on 16 Jul 2009

Sharing Memory Allocators while at the same time Handling Dynamic Media Type Changes

Sharing memory allocators between input and output pins is an important concept to keep performance of filter graph. Unlike more frequent scenario with different allocators, a filter (referred to as “middle filter” below) which has equal media types on input and output pins has an advantage to avoid memory-to-memory copy operation for every frame processed, by delivering downstream the buffer obtained from an upstream filter. With a high resolution video, at high rate, multiple streams running simultaneously this is the expense one would try to avoid for performance reasons.

Memory allocators are (or can be) shared by well known filters, such as Sample Grabber Filter, Infinite Tee Pin Filter and in-place transformation base filters (CTransInPlaceFilter Class).

Still handling Dynamic Format Changes (not only from video renderer filter) filters that share memory allocators may run into the problem of being notified of media type change. Because allocator are typically owned by another filter (e.g. Video Mixing Renderer Filter) and originally its buffer is queried by an upstream filter, the upstream filter obtains allocated buffer independently from the middle filter that shares memory allocators. If the upstream filter decides to never deliver this buffer, however the buffer has a media type attached (see AM_SAMPLE2_PROPERTIES::pMediaType), there is no way for the middle filter to learn about dynamic format change completed.

As a workaround for handling Format Changes from the Video Renderer, when resolution is not changed and it is only stride which might be extended, middle filter might be checking data size in lActual field and learn about the change from an increase in this value.

To be reliably notified on media type change the middle filter is to take extra measures while sharing the allocator. Instead using raw allocator obtained from one pin on another pin (typically output pin’s allocator to be used on an input pin), middle filter may be using an internal proxy object, which implements IMemAllocator interface and forward calls to internal IMemAllocator, obtained originally. Additionally to that, the proxy can check for attached media types on every buffer taken from the allocator, and once the change is noticed – at the moment upstream filter is requesting the buffer – the proxy has a timely chance to remember the new media type so that in the following IMemInputPin::Receive call this media type can be checked for the case upstream buffer decided to not deliver the buffer with attached media type.

if(IsSharingMemAllocators())
{
    // ...
    ATLASSERT((InputMediaSampleProperties.pMediaType != NULL) ^ !(InputMediaSampleProperties.dwSampleFlags & AM_SAMPLE_TYPECHANGED));
    {
        CRoCriticalSectionLock DataLock(GetDataCriticalSection());
        const CObjectPtr<CProxyMemAllocator>& pInputProxyMemAllocator = m_pInputPin->GetProxyMemAllocatorReference();
        CMediaType pMediaType;
        if(pInputProxyMemAllocator && pInputProxyMemAllocator->GetDynamicallyChangedMediaType(pMediaType, TRUE))
        {
            m_pInputPin->SetMediaType(pMediaType);
            m_pOutputPin->SetMediaType(pMediaType);
            // ...
        }
    }
    if(InputMediaSampleProperties.pMediaType)
    {
        m_pInputPin->SetMediaType(InputMediaSampleProperties.pMediaType);
        m_pOutputPin->SetMediaType(InputMediaSampleProperties.pMediaType);
        // ...
    }
    DeliverMediaSample(pMemInputPin, pInputMediaSample);
}

Published by Roman on 04 Jul 2009

Microsoft MVP Award in DirectShow/Media Foundation

As an appreciation of outstanding contributions in DShow/Media Foundation technical communities during the past year I was presented with the 2009 Microsoft® MVP Award, which I am very much pleased to receive.

Microsoft Most Valuable Professional Award

// Microsoft Most Valuable Professional Award

In an intention to accompany the post with useful DirectShow related information, here is the link list of most important DirectShow resources on the Internet:

Continue Reading »