Paso 5: Controlar eventos de sesión multimedia
Este tema es el paso 5 del tutorial Cómo reproducir archivos multimedia con Media Foundation. El código completo se muestra en el tema Ejemplo de reproducción de sesión multimedia.
Para obtener información sobre este tema, lea Generadores de eventos multimedia. Este tema contiene las siguientes secciones:
- Obtención de eventos de sesión
- MESessionTopologyStatus
- MEEndOfPresentation
- MENewPresentation
- Temas relacionados
Obtención de eventos de sesión
Para obtener eventos de la sesión multimedia, el objeto CPlayer llama al método IMFMediaEventGenerator::BeginGetEvent , como se muestra en paso 4: Crear la sesión multimedia. Este método es asincrónico, lo que significa que vuelve al autor de la llamada inmediatamente. Cuando se produce el siguiente evento de sesión, la sesión multimedia llama al método IMFAsyncCallback::Invoke del objeto CPlayer.
Es importante recordar que se llama a Invoke desde un subproceso de trabajo, no desde el subproceso de aplicación. Por lo tanto, la implementación de Invoke debe ser segura para varios subprocesos. Un enfoque sería proteger los datos de miembro con una sección crítica. Sin embargo, la CPlayer
clase muestra un enfoque alternativo:
- En el método Invoke , el objeto CPlayer envía un mensaje WM_APP_PLAYER_EVENT a la aplicación. El parámetro message es un puntero IMFMediaEvent .
- La aplicación recibe el mensaje WM_APP_PLAYER_EVENT .
- La aplicación llama al
CPlayer::HandleEvent
método y pasa el puntero IMFMediaEvent . - El
HandleEvent
método responde al evento .
El código siguiente muestra el 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;
}
El método Invoke realiza los pasos siguientes:
- Llame a IMFMediaEventGenerator::EndGetEvent para obtener el evento. Este método devuelve un puntero a la interfaz IMFMediaEvent .
- Llame a IMFMediaEvent::GetType para obtener el código de evento.
- Si el código de evento es MESessionClosed, llame a SetEvent para establecer el evento m_hCloseEvent . El motivo de este paso se explica en Paso 7: Apagar la sesión multimedia y también en los comentarios del código.
- Para todos los demás códigos de evento, llame a IMFMediaEventGenerator::BeginGetEvent para solicitar el siguiente evento.
- Publique un mensaje WM_APP_PLAYER_EVENT en la ventana.
El código siguiente muestra el método HandleEvent, al que se llama cuando la aplicación recibe el mensaje 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;
}
Este método llama a IMFMediaEvent::GetType para obtener el tipo de evento y IMFMediaEvent::GetStatus para obtener el éxito del código de error asociado al evento. La siguiente acción realizada depende del código de evento.
MESessionTopologyStatus
El evento MESessionTopologyStatus indica un cambio en el estado de la topología. El atributo MF_EVENT_TOPOLOGY_STATUS del objeto de evento contiene el estado. En este ejemplo, el único valor de interés es MF_TOPOSTATUS_READY, lo que indica que la reproducción está lista para iniciarse.
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;
}
El CPlayer::StartPlayback
método se muestra en el paso 6: Controlar la reproducción.
En este ejemplo también se llama a MFGetService para obtener la interfaz IMFVideoDisplayControl del representador de vídeo mejorado (EVR). Esta interfaz es necesaria para controlar el reintentos y el cambio de tamaño de la ventana de vídeo, que también se muestra en paso 6: Controlar la reproducción.
MEEndOfPresentation
El evento MEEndOfPresentation indica que la reproducción ha llegado al final del archivo. La sesión multimedia cambia automáticamente al estado detenido.
// 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
El evento MENewPresentation indica el inicio de una nueva presentación. Los datos del evento son un puntero IMFPresentationDescriptor para la nueva presentación.
En muchos casos, no recibirás este evento en absoluto. Si lo hace, use el puntero IMFPresentationDescriptor para crear una nueva topología de reproducción, como se muestra en paso 3: Abrir un archivo multimedia. A continuación, pone en cola la nueva topología en la sesión multimedia.
// 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;
}
Siguiente: Paso 6: Controlar la reproducción
Temas relacionados