Schritt 5: Behandeln von Mediensitzungsereignissen

Dieses Thema ist Schritt 5 des Tutorials Wiedergeben von Mediendateien mit Media Foundation. Der vollständige Code wird im Thema Media Session Playback Example (Beispiel für die Mediensitzungswiedergabe) angezeigt.

Hintergrundinformationen zu diesem Thema finden Sie unter Medienereignisgeneratoren. Dieses Thema enthält folgende Abschnitte:

Abrufen von Sitzungsereignissen

Um Ereignisse aus der Mediensitzung abzurufen, ruft das CPlayer-Objekt die IMFMediaEventGenerator::BeginGetEvent-Methode auf, wie in Schritt 4: Erstellen der Mediensitzung gezeigt. Diese Methode ist asynchron, d. h., sie wird sofort an den Aufrufer zurückgegeben. Wenn das nächste Sitzungsereignis auftritt, ruft die Mediensitzung die IMFAsyncCallback::Invoke-Methode des CPlayer-Objekts auf.

Beachten Sie, dass Invoke von einem Workerthread und nicht vom Anwendungsthread aufgerufen wird. Daher muss die Implementierung von Invoke multithreadsicher sein. Ein Ansatz wäre, die Memberdaten mit einem kritischen Abschnitt zu schützen. Die CPlayer -Klasse zeigt jedoch einen alternativen Ansatz an:

  1. In der Invoke-Methode sendet das CPlayer-Objekt eine WM_APP_PLAYER_EVENT Nachricht an die Anwendung. Der Nachrichtenparameter ist ein IMFMediaEvent-Zeiger .
  2. Die Anwendung empfängt die WM_APP_PLAYER_EVENT Meldung.
  3. Die Anwendung ruft die CPlayer::HandleEvent -Methode auf und übergibt den IMFMediaEvent-Zeiger .
  4. Die HandleEvent -Methode antwortet auf das Ereignis.

Der folgende Code zeigt die Invoke-Methode :

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

Die Invoke-Methode führt die folgenden Schritte aus:

  1. Rufen Sie IMFMediaEventGenerator::EndGetEvent auf, um das Ereignis abzurufen. Diese Methode gibt einen Zeiger auf die IMFMediaEvent-Schnittstelle zurück.
  2. Rufen Sie IMFMediaEvent::GetType auf, um den Ereigniscode abzurufen.
  3. Wenn der Ereigniscode MESessionClosed lautet, rufen Sie SetEvent auf, um das m_hCloseEvent-Ereignis festzulegen. Der Grund für diesen Schritt wird in Schritt 7: Herunterfahren der Mediensitzung sowie in den Codekommentaren erläutert.
  4. Rufen Sie für alle anderen Ereigniscodes IMFMediaEventGenerator::BeginGetEvent auf , um das nächste Ereignis anzufordern.
  5. Posten Sie eine WM_APP_PLAYER_EVENT Nachricht im Fenster.

Der folgende Code zeigt die HandleEvent-Methode, die aufgerufen wird, wenn die Anwendung die WM_APP_PLAYER_EVENT Meldung empfängt:

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

Diese Methode ruft IMFMediaEvent::GetType auf, um den Ereignistyp abzurufen, und IMFMediaEvent::GetStatus , um den erfolgreichen Fehlercode abzurufen, der dem Ereignis zugeordnet ist. Die nächste ausgeführte Aktion hängt vom Ereigniscode ab.

MESessionTopologyStatus

Das MESessionTopologyStatus-Ereignis signalisiert eine Änderung der status der Topologie. Das MF_EVENT_TOPOLOGY_STATUS-Attribut des Ereignisobjekts enthält die status. In diesem Beispiel ist der einzige Wert von Interesse MF_TOPOSTATUS_READY, der angibt, dass die Wiedergabe gestartet werden kann.

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

Die CPlayer::StartPlayback -Methode wird in Schritt 6: Steuern der Wiedergabe gezeigt.

In diesem Beispiel wird auch MFGetService aufgerufen, um die IMFVideoDisplayControl-Schnittstelle aus dem erweiterten Video renderer (EVR ) abzurufen. Diese Schnittstelle ist erforderlich, um das Neulackieren und Ändern der Größe des Videofensters zu verarbeiten, wie in Schritt 6: Steuern der Wiedergabe gezeigt.

MEEndOfPresentation

Das MEEndOfPresentation-Ereignis signalisiert, dass die Wiedergabe das Ende der Datei erreicht hat. Die Mediensitzung wechselt automatisch wieder in den Status "Beendet".

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

Das MENewPresentation-Ereignis signalisiert den Beginn einer neuen Präsentation. Die Ereignisdaten sind ein IMFPresentationDescriptor-Zeiger für die neue Präsentation.

In vielen Fällen erhalten Sie dieses Ereignis überhaupt nicht. Verwenden Sie in diesem Fall den IMFPresentationDescriptor-Zeiger , um eine neue Wiedergabetopologie zu erstellen, wie in Schritt 3: Öffnen einer Mediendatei gezeigt. Stellen Sie dann die neue Topologie in der Mediensitzung in die Warteschlange.

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

Weiter: Schritt 6: Steuern der Wiedergabe

Audio-/Videowiedergabe

Wiedergeben von Mediendateien mit Media Foundation