Passaggio 5: Gestire gli eventi della sessione multimediale

Questo argomento è il passaggio 5 dell'esercitazione Come riprodurre file multimediali con Media Foundation. Il codice completo è illustrato nell'argomento Esempio di riproduzione di sessioni multimediali.

Per informazioni di base su questo argomento, leggere Generatori di eventi multimediali. In questo argomento sono incluse le sezioni seguenti:

Recupero di eventi di sessione

Per ottenere eventi dalla sessione multimediale, l'oggetto CPlayer chiama il metodo IMFMediaEventGenerator::BeginGetEvent , come illustrato nel passaggio 4: Creare la sessione multimediale. Questo metodo è asincrono, ovvero restituisce immediatamente al chiamante. Quando si verifica l'evento di sessione successivo, la sessione multimediale chiama il metodo IMFAsyncCallback::Invoke dell'oggetto CPlayer.

È importante ricordare che Invoke viene chiamato da un thread di lavoro, non dal thread dell'applicazione. Pertanto, l'implementazione di Invoke deve essere multithread-safe. Un approccio consiste nel proteggere i dati dei membri con una sezione critica. Tuttavia, la CPlayer classe mostra un approccio alternativo:

  1. Nel metodo Invoke l'oggetto CPlayer invia un messaggio WM_APP_PLAYER_EVENT all'applicazione. Il parametro message è un puntatore IMFMediaEvent .
  2. L'applicazione riceve il messaggio WM_APP_PLAYER_EVENT .
  3. L'applicazione chiama il CPlayer::HandleEvent metodo , passando il puntatore IMFMediaEvent .
  4. Il HandleEvent metodo risponde all'evento.

Il codice seguente mostra il metodo 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;
}

Il metodo Invoke esegue i passaggi seguenti:

  1. Chiamare IMFMediaEventGenerator::EndGetEvent per ottenere l'evento. Questo metodo restituisce un puntatore all'interfaccia IMFMediaEvent .
  2. Chiamare IMFMediaEvent::GetType per ottenere il codice dell'evento.
  3. Se il codice evento è MESessionClosed, chiama SetEvent per impostare l'evento m_hCloseEvent . Il motivo di questo passaggio è illustrato nel passaggio 7: Arrestare la sessione multimediale e anche nei commenti del codice.
  4. Per tutti gli altri codici evento, chiamare IMFMediaEventGenerator::BeginGetEvent per richiedere l'evento successivo.
  5. Pubblicare un messaggio di WM_APP_PLAYER_EVENT nella finestra.

Il codice seguente mostra il metodo HandleEvent, che viene chiamato quando l'applicazione riceve il messaggio di 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;
}

Questo metodo chiama IMFMediaEvent::GetType per ottenere il tipo di evento e IMFMediaEvent::GetStatus per ottenere l'esito positivo del codice di errore associato all'evento. L'azione successiva eseguita dipende dal codice dell'evento.

MESessionTopologyStatus

L'evento MESessionTopologyStatus segnala una modifica dello stato della topologia. L'attributo MF_EVENT_TOPOLOGY_STATUS dell'oggetto evento contiene lo stato. Per questo esempio, l'unico valore di interesse è MF_TOPOSTATUS_READY, che indica che la riproduzione è pronta per l'avvio.

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

Il CPlayer::StartPlayback metodo viene visualizzato nel passaggio 6: Controllare la riproduzione.

Questo esempio chiama anche MFGetService per ottenere l'interfaccia IMFVideoDisplayControldall'EVR (Enhanced Video Renderer ). Questa interfaccia è necessaria per gestire l'aggiornamento e il ridimensionamento della finestra video, illustrata anche nel passaggio 6: Controllare la riproduzione.

MEEndOfPresentation

L'evento MEEndOfPresentation segnala che la riproduzione ha raggiunto la fine del file. La sessione multimediale torna automaticamente allo stato arrestato.

//  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

L'evento MENewPresentation segnala l'inizio di una nuova presentazione. I dati dell'evento sono un puntatore IMFPresentationDescriptor per la nuova presentazione.

In molti casi, non si riceverà affatto questo evento. In tal caso, usare il puntatore IMFPresentationDescriptor per creare una nuova topologia di riproduzione, come illustrato nel passaggio 3: Aprire un file multimediale. Accodare quindi la nuova topologia nella sessione multimediale.

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

Passaggio 6: Controllare la riproduzione

Riproduzione di audio/video

Come riprodurre file multimediali con Media Foundation