Шаг 5. Обработка событий сеанса мультимедиа
Этот раздел является шагом 5 руководства По воспроизведению файлов мультимедиа с помощью Media Foundation. Полный код показан в разделе Пример воспроизведения сеанса мультимедиа.
Дополнительные сведения по этой теме см. в статье Генераторы событий мультимедиа. Этот раздел состоит из следующих подразделов.
- Получение событий сеанса
- MESessionTopologyStatus
- MEEndOfPresentation
- MENewPresentation
- Связанные темы
Получение событий сеанса
Чтобы получить события из сеанса мультимедиа, объект CPlayer вызывает метод IMFMediaEventGenerator::BeginGetEvent , как показано в разделе Шаг 4. Создание сеанса мультимедиа. Этот метод является асинхронным, то есть немедленно возвращается вызывающему объекту. При возникновении события следующего сеанса сеанс мультимедиа вызывает метод IMFAsyncCallback::Invoke объекта CPlayer.
Важно помнить, что invoke вызывается из рабочего потока, а не из потока приложения. Таким образом, реализация Invoke должна быть многопоточной. Один из подходов — защитить данные-члены с помощью критического раздела. Однако класс CPlayer
демонстрирует альтернативный подход:
- В методе Invoke объект CPlayer отправляет WM_APP_PLAYER_EVENT сообщение в приложение. Параметр сообщения является указателем IMFMediaEvent .
- Приложение получает сообщение WM_APP_PLAYER_EVENT .
- Приложение вызывает
CPlayer::HandleEvent
метод , передав указатель IMFMediaEvent . - Метод
HandleEvent
реагирует на событие .
В следующем коде показан метод 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;
}
Метод Invoke выполняет следующие действия.
- Вызовите IMFMediaEventGenerator::EndGetEvent , чтобы получить событие. Этот метод возвращает указатель на интерфейс IMFMediaEvent .
- Вызовите IMFMediaEvent::GetType , чтобы получить код события.
- Если код события — MESessionClosed, вызовите SetEvent, чтобы задать событие m_hCloseEvent . Причина этого шага описана в разделе Шаг 7. Завершение сеанса мультимедиа, а также в комментариях к коду.
- Для всех остальных кодов событий вызовите IMFMediaEventGenerator::BeginGetEvent , чтобы запросить следующее событие.
- Опубликуй в окне сообщение WM_APP_PLAYER_EVENT .
В следующем коде показан метод HandleEvent, который вызывается, когда приложение получает сообщение 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;
}
Этот метод вызывает IMFMediaEvent::GetType для получения типа события и IMFMediaEvent::GetStatus , чтобы получить код сбоя, связанный с событием. Следующее действие зависит от кода события.
MESessionTopologyStatus
Событие MESessionTopologyStatus сигнализирует об изменении состояния топологии. Атрибут MF_EVENT_TOPOLOGY_STATUS объекта события содержит состояние . В этом примере единственным интересующим значением является MF_TOPOSTATUS_READY, которое указывает, что воспроизведение готово к запуску.
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;
}
Метод CPlayer::StartPlayback
показан в разделе Шаг 6. Управление воспроизведением.
В этом примере также вызывается MFGetService для получения интерфейса IMFVideoDisplayControl из расширенного отрисовщика видео (EVR). Этот интерфейс необходим для обработки перерисовки и изменения размера окна видео, также показанного в разделе Шаг 6. Управление воспроизведением.
MEEndOfPresentation
Событие MEEndOfPresentation сообщает, что воспроизведение достигло конца файла. Сеанс мультимедиа автоматически переключается обратно в остановленное состояние.
// 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
Событие MENewPresentation сигнализирует о начале новой презентации. Данные события — это указатель IMFPresentationDescriptor для новой презентации.
Во многих случаях это событие не будет получено. В этом случае используйте указатель IMFPresentationDescriptor , чтобы создать новую топологию воспроизведения, как показано в разделе Шаг 3. Открытие файла мультимедиа. Затем поставьте новую топологию в очередь в сеансе мультимедиа.
// 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;
}
Далее: Шаг 6. Управление воспроизведением
Связанные темы