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 generali su questo argomento, vedere generatori di eventi multimediali . Questo argomento contiene le sezioni seguenti:
- Ottenere eventi di sessione
- MESessionTopologyStatus
- MEFinePresentazione
- MENewPresentation
- argomenti correlati
Recuperare eventi di sessione
Per ottenere eventi dalla sessione multimediale, l'oggetto CPlayer chiama il metodo IMFMediaEventGenerator::BeginGetEvent, come illustrato in 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 classe CPlayer
mostra un approccio alternativo:
- Nel metodo Invoke l'oggetto CPlayer invia un messaggio WM_APP_PLAYER_EVENT all'applicazione. Il parametro message è un puntatore IMFMediaEvent.
- L'applicazione riceve il messaggio WM_APP_PLAYER_EVENT.
- L'applicazione chiama il metodo
CPlayer::HandleEvent
, passando il puntatore IMFMediaEvent. - Il metodo
HandleEvent
risponde all'evento.
Il codice seguente illustra 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 metodoInvokeesegue i passaggi seguenti:
- Chiamare IMFMediaEventGenerator::EndGetEvent per ottenere l'evento. Questo metodo restituisce un puntatore all'interfaccia IMFMediaEvent.
- Chiamare IMFMediaEvent::GetType per ottenere il codice dell'evento.
- Se il codice evento è MESessionClosed, chiamare SetEvent per impostare l'evento m_hCloseEvent. Il motivo di questo passaggio è illustrato in Passaggio 7: Arrestare la sessione multimedialee anche nei commenti del codice.
- Per tutti gli altri codici evento, chiamare IMFMediaEventGenerator::BeginGetEvent per richiedere l'evento successivo.
- Invia un messaggio WM_APP_PLAYER_EVENT alla finestra.
Il codice seguente illustra il metodo HandleEvent, chiamato quando l'applicazione riceve il messaggio 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 metodo CPlayer::StartPlayback
viene visualizzato in Passaggio 6: Controllo della riproduzione.
Questo esempio chiama anche MFGetService per ottenere l'interfaccia IMFVideoDisplayControl dal Enhanced Video Renderer (EVR). Questa interfaccia è necessaria per gestire il rinfresco e il ridimensionamento della finestra video, come mostrato anche in Passaggio 6: Controllare la riproduzione.
MEFineDellaPresentazione
L'evento MEEndOfPresentation segnala che la riproduzione ha raggiunto la fine del file. La sessione multimediale torna automaticamente allo stato di arresto.
// Handler for MEEndOfPresentation event.
HRESULT CPlayer::OnPresentationEnded(IMFMediaEvent *pEvent)
{
// The session puts itself into the stopped state automatically.
m_state = Stopped;
return S_OK;
}
MENuovaPresentazione
L'evento MENewPresentation segnala l'inizio di una nuova presentazione. I dati dell'evento sono un puntatore di tipo IMFPresentationDescriptor per la nuova presentazione.
In molti casi, non riceverai affatto questo evento. In questo caso, usare il puntatore IMFPresentationDescriptor per creare una nuova topologia di riproduzione, come illustrato in Passaggio 3: Aprire un file multimediale. Quindi accodare 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;
}
Avanti: Passaggio 6: Controllare la riproduzione
Argomenti correlati