Etapa 5: Manipular eventos de sessão de mídia
Este tópico é a etapa 5 do tutorial How to Play Media Files with Media Foundation. O código completo é mostrado no tópico Exemplo de Reprodução da Sessão de Mídia.
Para obter informações sobre este tópico, leia Geradores de Eventos de Mídia. Este tópico contém as seguintes seções:
- Obtendo eventos de sessão
- MESessionTopologyStatus
- MEEndOfPresentation
- MENewPresentation
- Tópicos relacionados
Obtendo eventos de sessão
Para obter eventos da Sessão de Mídia, o objeto CPlayer chama o método IMFMediaEventGenerator::BeginGetEvent , conforme mostrado na Etapa 4: Criar a Sessão de Mídia. Esse método é assíncrono, o que significa que ele retorna ao chamador imediatamente. Quando o próximo evento de sessão ocorrer, a Sessão de Mídia chamará o método IMFAsyncCallback::Invoke do objeto CPlayer.
É importante lembrar que Invoke é chamado de um thread de trabalho, não do thread do aplicativo. Portanto, a implementação de Invoke deve ser multithread-safe. Uma abordagem seria proteger os dados do membro com uma seção crítica. No entanto, a CPlayer
classe mostra uma abordagem alternativa:
- No método Invoke , o objeto CPlayer posta uma mensagem WM_APP_PLAYER_EVENT para o aplicativo. O parâmetro de mensagem é um ponteiro IMFMediaEvent .
- O aplicativo recebe a mensagem WM_APP_PLAYER_EVENT .
- O aplicativo chama o
CPlayer::HandleEvent
método, passando o ponteiro IMFMediaEvent . - O
HandleEvent
método responde ao evento.
O código a seguir mostra o método Invoke :
// Callback for the asynchronous BeginGetEvent method.
HRESULT CPlayer::Invoke(IMFAsyncResult *pResult)
{
MediaEventType meType = MEUnknown; // Event type
IMFMediaEvent *pEvent = NULL;
// Get the event from the event queue.
HRESULT hr = m_pSession->EndGetEvent(pResult, &pEvent);
if (FAILED(hr))
{
goto done;
}
// Get the event type.
hr = pEvent->GetType(&meType);
if (FAILED(hr))
{
goto done;
}
if (meType == MESessionClosed)
{
// The session was closed.
// The application is waiting on the m_hCloseEvent event handle.
SetEvent(m_hCloseEvent);
}
else
{
// For all other events, get the next event in the queue.
hr = m_pSession->BeginGetEvent(this, NULL);
if (FAILED(hr))
{
goto done;
}
}
// Check the application state.
// If a call to IMFMediaSession::Close is pending, it means the
// application is waiting on the m_hCloseEvent event and
// the application's message loop is blocked.
// Otherwise, post a private window message to the application.
if (m_state != Closing)
{
// Leave a reference count on the event.
pEvent->AddRef();
PostMessage(m_hwndEvent, WM_APP_PLAYER_EVENT,
(WPARAM)pEvent, (LPARAM)meType);
}
done:
SafeRelease(&pEvent);
return S_OK;
}
O método Invoke executa as seguintes etapas:
- Chame IMFMediaEventGenerator::EndGetEvent para obter o evento. Esse método retorna um ponteiro para a interface IMFMediaEvent .
- Chame IMFMediaEvent::GetType para obter o código do evento.
- Se o código de evento for MESessionClosed, chame SetEvent para definir o evento m_hCloseEvent . O motivo dessa etapa é explicado na Etapa 7: Desligar a Sessão de Mídia e também nos comentários de código.
- Para todos os outros códigos de evento, chame IMFMediaEventGenerator::BeginGetEvent para solicitar o próximo evento.
- Poste uma mensagem de WM_APP_PLAYER_EVENT na janela.
O código a seguir mostra o método HandleEvent, que é chamado quando o aplicativo recebe a mensagem WM_APP_PLAYER_EVENT :
HRESULT CPlayer::HandleEvent(UINT_PTR pEventPtr)
{
HRESULT hrStatus = S_OK;
MediaEventType meType = MEUnknown;
IMFMediaEvent *pEvent = (IMFMediaEvent*)pEventPtr;
if (pEvent == NULL)
{
return E_POINTER;
}
// Get the event type.
HRESULT hr = pEvent->GetType(&meType);
if (FAILED(hr))
{
goto done;
}
// Get the event status. If the operation that triggered the event
// did not succeed, the status is a failure code.
hr = pEvent->GetStatus(&hrStatus);
// Check if the async operation succeeded.
if (SUCCEEDED(hr) && FAILED(hrStatus))
{
hr = hrStatus;
}
if (FAILED(hr))
{
goto done;
}
switch(meType)
{
case MESessionTopologyStatus:
hr = OnTopologyStatus(pEvent);
break;
case MEEndOfPresentation:
hr = OnPresentationEnded(pEvent);
break;
case MENewPresentation:
hr = OnNewPresentation(pEvent);
break;
default:
hr = OnSessionEvent(pEvent, meType);
break;
}
done:
SafeRelease(&pEvent);
return hr;
}
Esse método chama IMFMediaEvent::GetType para obter o tipo de evento e IMFMediaEvent::GetStatus para obter o sucesso do código de falha associado ao evento. A próxima ação executada depende do código do evento.
MESessionTopologyStatus
O evento MESessionTopologyStatus sinaliza uma alteração no status da topologia. O atributo MF_EVENT_TOPOLOGY_STATUS do objeto de evento contém o status. Para este exemplo, o único valor de interesse é MF_TOPOSTATUS_READY, o que indica que a reprodução está pronta para começar.
HRESULT CPlayer::OnTopologyStatus(IMFMediaEvent *pEvent)
{
UINT32 status;
HRESULT hr = pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status);
if (SUCCEEDED(hr) && (status == MF_TOPOSTATUS_READY))
{
SafeRelease(&m_pVideoDisplay);
// Get the IMFVideoDisplayControl interface from EVR. This call is
// expected to fail if the media file does not have a video stream.
(void)MFGetService(m_pSession, MR_VIDEO_RENDER_SERVICE,
IID_PPV_ARGS(&m_pVideoDisplay));
hr = StartPlayback();
}
return hr;
}
O CPlayer::StartPlayback
método é mostrado na Etapa 6: Reprodução de Controle.
Este exemplo também chama MFGetService para obter a interface IMFVideoDisplayControl do Renderizador de Vídeo Avançado (EVR). Essa interface é necessária para lidar com a repintação e redimensionamento da janela de vídeo, também mostrada na Etapa 6: Reprodução de controle.
MEEndOfPresentation
O evento MEEndOfPresentation sinaliza que a reprodução chegou ao final do arquivo. A Sessão de Mídia volta automaticamente para o estado interrompido.
// Handler for MEEndOfPresentation event.
HRESULT CPlayer::OnPresentationEnded(IMFMediaEvent *pEvent)
{
// The session puts itself into the stopped state automatically.
m_state = Stopped;
return S_OK;
}
MENewPresentation
O evento MENewPresentation sinaliza o início de uma nova apresentação. Os dados do evento são um ponteiro IMFPresentationDescriptor para a nova apresentação.
Em muitos casos, você não receberá esse evento. Se você fizer isso, use o ponteiro IMFPresentationDescriptor para criar uma nova topologia de reprodução, conforme mostrado na Etapa 3: Abrir um Arquivo de Mídia. Em seguida, enfileira a nova topologia na Sessão de Mídia.
// Handler for MENewPresentation event.
//
// This event is sent if the media source has a new presentation, which
// requires a new topology.
HRESULT CPlayer::OnNewPresentation(IMFMediaEvent *pEvent)
{
IMFPresentationDescriptor *pPD = NULL;
IMFTopology *pTopology = NULL;
// Get the presentation descriptor from the event.
HRESULT hr = GetEventObject(pEvent, &pPD);
if (FAILED(hr))
{
goto done;
}
// Create a partial topology.
hr = CreatePlaybackTopology(m_pSource, pPD, m_hwndVideo,&pTopology);
if (FAILED(hr))
{
goto done;
}
// Set the topology on the media session.
hr = m_pSession->SetTopology(0, pTopology);
if (FAILED(hr))
{
goto done;
}
m_state = OpenPending;
done:
SafeRelease(&pTopology);
SafeRelease(&pPD);
return S_OK;
}
Próximo: Etapa 6: Reprodução de controle
Tópicos relacionados