Compartir a través de


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

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:

  1. 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 .
  2. La aplicación recibe el mensaje WM_APP_PLAYER_EVENT .
  3. La aplicación llama al CPlayer::HandleEvent método y pasa el puntero IMFMediaEvent .
  4. 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:

  1. Llame a IMFMediaEventGenerator::EndGetEvent para obtener el evento. Este método devuelve un puntero a la interfaz IMFMediaEvent .
  2. Llame a IMFMediaEvent::GetType para obtener el código de evento.
  3. 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.
  4. Para todos los demás códigos de evento, llame a IMFMediaEventGenerator::BeginGetEvent para solicitar el siguiente evento.
  5. 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

Reproducción de audio y vídeo

Cómo reproducir archivos multimedia con Media Foundation