5단계: 미디어 세션 이벤트 처리
이 항목은 Media Foundation을 사용하여 미디어 파일을 재생하는 방법 자습서의 5단계입니다. 전체 코드는 미디어 세션 재생 예제 항목에 나와 있습니다.
이 항목에 대한 배경 정보는 미디어 이벤트 생성기를 참조하세요. 이 항목에는 다음과 같은 섹션이 포함되어 있습니다.
세션 이벤트 가져오기
미디어 세션에서 이벤트를 가져오기 위해 CPlayer 개체는 4단계: 미디어 세션 만들기에 표시된 대로 IMFMediaEventGenerator::BeginGetEvent 메서드를 호출합니다. 이 메서드는 비동기적입니다. 즉, 호출자에게 즉시 반환됩니다. 다음 세션 이벤트가 발생하면 미디어 세션은 CPlayer 개체의 IMFAsyncCallback::Invoke 메서드를 호출합니다.
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 메시지를 게시합니다.
다음 코드는 애플리케이션이 WM_APP_PLAYER_EVENT 메시지를 받을 때 호출되는 HandleEvent 메서드를 보여 줍니다.
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를 호출하여 EVR(Enhanced Video Renderer)에서 IMFVideoDisplayControl 인터페이스를 가져옵니다. 이 인터페이스는 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 포인터입니다.
대부분의 경우 이 이벤트는 전혀 수신되지 않습니다. 이 경우 3단계: 미디어 파일 열기에 표시된 것처럼 IMFPresentationDescriptor 포인터를 사용하여 새 재생 토폴로지를 만듭니다. 그런 다음 미디어 세션에서 새 토폴로지를 큐에 대기합니다.
// 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단계: 컨트롤 재생
관련 항목