Chamando métodos assíncronos

No Media Foundation, muitas operações são executadas de forma assíncrona. As operações assíncronas podem melhorar o desempenho, pois não bloqueiam o thread de chamada. Uma operação assíncrona no Media Foundation é definida por um par de métodos com a convenção de nomenclatura Begin... e End..... Juntos, esses dois métodos definem uma operação de leitura assíncrona no fluxo.

Para iniciar uma operação assíncrona, chame o método Begin . O método Begin sempre contém pelo menos dois parâmetros:

  • Um ponteiro para a interface IMFAsyncCallback . O aplicativo deve implementar essa interface.
  • Um ponteiro para um objeto de estado opcional.

A interface IMFAsyncCallback notifica o aplicativo quando a operação é concluída. Para obter um exemplo de como implementar essa interface, consulte Implementando o retorno de chamada assíncrono. O objeto state é opcional. Se especificado, ele deve implementar a interface IUnknown . Você pode usar o objeto state para armazenar todas as informações de estado necessárias para o retorno de chamada. Se você não precisar de informações de estado, poderá definir esse parâmetro como NULL. O método Begin pode conter parâmetros adicionais específicos a esse método.

Se o método Begin retornar um código de falha, isso significa que a operação falhou imediatamente e o retorno de chamada não será invocado. Se o método Begin for bem-sucedido, isso significa que a operação está pendente e o retorno de chamada será invocado quando a operação for concluída.

Por exemplo, o método IMFByteStream::BeginRead inicia uma operação de leitura assíncrona em um fluxo de bytes:

    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);
    }

O método de retorno de chamada é IMFAsyncCallback::Invoke. Ele tem um único parâmetro, pAsyncResult, que é um ponteiro para a interface IMFAsyncResult . Para concluir a operação assíncrona, chame o método End que corresponde ao método Begin assíncrono. O método End sempre usa um ponteiro IMFAsyncResult . Sempre passe o mesmo ponteiro que você recebeu no método Invoke . A operação assíncrona não é concluída até que você chame o método End . Você pode chamar o método End de dentro do método Invoke ou chamá-lo de outro thread.

Por exemplo, para concluir o IMFByteStream::BeginRead em um fluxo de bytes, chame IMFByteStream::EndRead:

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

        SetEvent(m_hEvent);

        return S_OK;
    }

O valor retornado do método End indica o status da operação assíncrona. Na maioria dos casos, seu aplicativo executará algum trabalho após a conclusão da operação assíncrona, seja ela concluída com êxito ou não. Você tem várias opções:

  • Faça o trabalho dentro do método Invoke . Essa abordagem é aceitável, desde que seu aplicativo nunca bloqueie o thread de chamada ou aguarde qualquer coisa dentro do método Invoke . Se você bloquear o thread de chamada, poderá bloquear o pipeline de streaming. Por exemplo, você pode atualizar algumas variáveis de estado, mas não executar uma operação de leitura síncrona ou aguardar em um objeto mutex.
  • No método Invoke , defina um evento e retorne imediatamente. O thread de chamada do aplicativo aguarda o evento e faz o trabalho quando o evento é sinalizado.
  • No método Invoke , poste uma mensagem de janela privada na janela do aplicativo e retorne imediatamente. O aplicativo faz o trabalho em seu loop de mensagem. (Certifique-se de postar a mensagem chamando PostMessage, não SendMessage, porque SendMessage pode bloquear o thread de retorno de chamada.)

É importante lembrar que Invoke é chamado de outro thread. Seu aplicativo é responsável por garantir que qualquer trabalho executado dentro do Invoke seja thread-safe.

Por padrão, o thread que chama Invoke não faz suposições sobre o comportamento do objeto de retorno de chamada. Opcionalmente, você pode implementar o método IMFAsyncCallback::GetParameters para retornar informações sobre sua implementação de retorno de chamada. Caso contrário, esse método poderá retornar E_NOTIMPL:

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

Se você especificou um objeto de estado no método Begin , poderá recuperá-lo de dentro do método de retorno de chamada chamando IMFAsyncResult::GetState.

Métodos de retorno de chamada assíncronos