Calling Asynchronous Methods

In Media Foundation, many operations are performed asynchronously. Asynchronous operations can improve performance, because they do not block the calling thread. An asynchronous operation in Media Foundation is defined by a pair of methods with the naming convention Begin... and End..... Together, these two methods define an asynchronous read operation on the stream.

To start an asynchronous operation, call the Begin method. The Begin method always contains at least two parameters:

  • A pointer to the IMFAsyncCallback interface. The application must implement this interface.
  • A pointer to an optional state object.

The IMFAsyncCallback interface notifies the application when the operation is completed. For an example of how to implement this interface, see Implementing the Asynchronous Callback. The state object is optional. If specified, it must implement the IUnknown interface. You can use the state object to store any state information that is needed by your callback. If you do not need state information, you can set this parameter to NULL. The Begin method might contain additional parameters that are specific to that method.

If the Begin method returns a failure code, it means the operation has failed immediately, and the callback will not be invoked. If the Begin method succeeds, it means the operation is pending, and the callback will be invoked when the operation completes.

For example, the IMFByteStream::BeginRead method starts an asynchronous read operation on a byte stream:

    BYTE data[DATA_SIZE];
    ULONG cbRead = 0;

    CMyCallback *pCB = new (std::nothrow) CMyCallback(pStream, &hr);

    if (pCB == NULL)
    {
        hr = E_OUTOFMEMORY;
    }

    // Start an asynchronous request to read data.
    if (SUCCEEDED(hr))
    {
        hr = pStream->BeginRead(data, DATA_SIZE, pCB, NULL);
    }

The callback method is IMFAsyncCallback::Invoke. It has a single parameter, pAsyncResult, which is a pointer to the IMFAsyncResult interface. To complete the asynchronous operation, call the End method that matches the asynchronous Begin method. The End method always takes an IMFAsyncResult pointer. Always pass in the same pointer that you received in the Invoke method. The asynchronous operation is not complete until you call the End method. You may call the End method from inside the Invoke method, or you can call it from another thread.

For example, to complete the IMFByteStream::BeginRead on a byte stream, call IMFByteStream::EndRead:

    STDMETHODIMP Invoke(IMFAsyncResult* pResult)
    {
        m_hrStatus = m_pStream->EndRead(pResult, &m_cbRead);

        SetEvent(m_hEvent);

        return S_OK;
    }

The return value of the End method indicates the status of the asynchronous operation. In most cases, your application will perform some work after the asynchronous operation completes, whether it completes successfully or not. You have several options:

  • Do the work inside the Invoke method. This approach is acceptable as long as your application never blocks the calling thread or waits for anything inside the Invoke method. If you block the calling thread, you might block the streaming pipeline. For example, you might update some state variables, but do not perform a synchronous read operation or wait on a mutex object.
  • In the Invoke method, set an event and return immediately. The application's calling thread waits for the event and does the work when the event is signaled.
  • In the Invoke method, post a private window message to your application window and return immediately. The application does the work on its message loop. (Make sure to post the message by calling PostMessage, not SendMessage, because SendMessage can block the callback thread.)

It is important to remember that Invoke is called from another thread. Your application is responsible for ensuring that any work performed inside Invoke is thread-safe.

By default, the thread that calls Invoke makes no assumptions about the behavior of your callback object. Optionally, you can implement the IMFAsyncCallback::GetParameters method to return information about your callback implementation. Otherwise, this method can return E_NOTIMPL:

    STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue)
    {
        // Implementation of this method is optional.
        return E_NOTIMPL;
    }

If you specified a state object in the Begin method, you can retrieve it from inside your callback method by calling IMFAsyncResult::GetState.

Asynchronous Callback Methods