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 Mediensitzung – Wiedergabebeispielgezeigt.
Hintergrundinformationen zu diesem Thema finden Sie unter Media Event Generators. Dieses Thema enthält folgende Abschnitte:
- Abrufen von Sitzungsereignissen
- MESessionTopologyStatus
- MEEndOfPresentation
- MENewPresentation
- Zugehörige Themen
Abrufen von Sitzungsereignissen
Um Ereignisse aus der Mediensitzung abzurufen, ruft das CPlayer-Objekt die METHODE SFCMediaEventGenerator::BeginGetEvent auf, wie in Schritt 4: Erstellen der Mediensitzunggezeigt. Diese Methode ist asynchron, d. h. sie kehrt sofort zum Aufrufer zurück. Wenn das nächste Sitzungsereignis auftritt, ruft die Mediensitzung die METHODE AWAITAsyncCallback::Invoke des CPlayer-Objekts auf.
Es ist wichtig zu beachten, dass Invoke von einem Arbeitsthread aufgerufen wird, nicht vom Anwendungsthread. Daher muss die Implementierung von Invoke multithreadsicher sein. Ein Ansatz wäre, die Memberdaten mit einem kritischen Abschnitt zu schützen. Die -Klasse zeigt jedoch CPlayer einen alternativen Ansatz:
- In der Invoke-Methode sendet das CPlayer-Objekt eine WM APP PLAYER _ _ _ EVENT-Nachricht an die Anwendung. Der Message-Parameter ist ein POINTERMediaEvent-Zeiger.
- Die Anwendung empfängt die WM _ APP PLAYER _ _ EVENT-Nachricht.
- Die Anwendung ruft die -Methode auf
CPlayer::HandleEventund übergibt den POINTERMediaEvent-Zeiger. - 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:
- Rufen Sie DENMEDIAEventGenerator::EndGetEvent auf, um das Ereignis abzurufen. Diese Methode gibt einen Zeiger auf die INTERFACESMediaEvent-Schnittstelle zurück.
- Rufen Sie ÜBERMEDIAEVENT::GetType auf, um den Ereigniscode abzurufen.
- Wenn der Ereigniscode MESessionClosedist, rufen Sie SetEvent auf, um das m _ hCloseEvent-Ereignis festzulegen. Der Grund für diesen Schritt wird in Schritt 7: Herunterfahren der Mediensitzungund auch in den Codekommentaren erläutert.
- Rufen Sie für alle anderen Ereigniscodes DIE AKTION ÜBERMEDIAEVENTGenerator::BeginGetEvent auf, um das nächste Ereignis anzufordern.
- Posten Sie eine WM _ APP _ _ PLAYER-EREIGNISmeldung im Fenster.
Der folgende Code zeigt die HandleEvent-Methode, die aufgerufen wird, wenn die Anwendung die WM _ APP PLAYER _ _ EVENT-Nachricht 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 DASMEDIAEvent::GetType auf, um den Ereignistyp abzurufen, und DENMEDIAEvent::GetStatus, um den Erfolg des Fehlercodes abzurufen, der dem Ereignis zugeordnet ist. Die nächste ausgeführte Aktion hängt vom Ereigniscode ab.
MESessionTopologyStatus
Das MESessionTopologyStatus-Ereignis signalisiert eine Änderung des Status der Topologie. Das MF _ EVENT _ TOPOLOGY _ STATUS-Attribut des Ereignisobjekts enthält den Status. In diesem Beispiel ist MF _ TOPOSTATUS _ READY der einzige interessante Wert, der angibt, dass die Wiedergabe für den Start bereit ist.
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 Wiedergabegezeigt.
In diesem Beispiel wird auch MFGetService aufgerufen, um die SCHNITTSTELLE "THICKNESSVideoDisplayControl" vom Enhanced Video Renderer (EVR) abzurufen. Diese Schnittstelle ist erforderlich, um das Neumalieren und Ändern der Größe des Videofensters zu verarbeiten. Dies wird auch in Schritt 6: Steuern der Wiedergabegezeigt.
MEEndOfPresentation
Das MEEndOfPresentation-Ereignis signalisiert, dass die Wiedergabe das Ende der Datei erreicht hat. Die Mediensitzung wechselt automatisch wieder in den Zustand "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 POINTERPresentationDescriptor-Zeiger für die neue Präsentation.
In vielen Fällen erhalten Sie dieses Ereignis überhaupt nicht. Wenn Sie dies tun, verwenden Sie den POINTERPresentationDescriptor-Zeiger, um eine neue Wiedergabetopologie zu erstellen, wie in Schritt 3: Öffnen einer Mediendateigezeigt. Anschließend wird die neue Topologie in der Mediensitzung in die Warteschlange aufgenommen.
// 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